bitkeeper revision 1.640.1.1 (3fd767739FyOQWwEh4yC5GX2GHC2wA)
authoriap10@labyrinth.cl.cam.ac.uk <iap10@labyrinth.cl.cam.ac.uk>
Wed, 10 Dec 2003 18:35:31 +0000 (18:35 +0000)
committeriap10@labyrinth.cl.cam.ac.uk <iap10@labyrinth.cl.cam.ac.uk>
Wed, 10 Dec 2003 18:35:31 +0000 (18:35 +0000)
Update e1000 driver based on Intel's 5.2.16 release.

.rootkeys
xen/drivers/net/e1000/e1000.h
xen/drivers/net/e1000/e1000_ethtool.c
xen/drivers/net/e1000/e1000_hw.c
xen/drivers/net/e1000/e1000_hw.h
xen/drivers/net/e1000/e1000_main.c
xen/drivers/net/e1000/e1000_osdep.h
xen/drivers/net/e1000/e1000_param.c
xen/drivers/net/e1000/kcompat.c [new file with mode: 0644]
xen/drivers/net/e1000/kcompat.h [new file with mode: 0644]

index b3264c981129478943861a775d567bbc24164e8a..792d80a17fedd1b812c2996e1fb2d95797ce71fb 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3e4540ccvQ9Dtoh9tV-L3ULUwN9X7g xen/drivers/net/e1000/e1000_main.c
 3e4540cc3t7_y-YLeyMG2pX9xtdXPA xen/drivers/net/e1000/e1000_osdep.h
 3e4540cct_8Ig-Y1W_vM2gS_u7mC0A xen/drivers/net/e1000/e1000_param.c
+3fd76772aP8tdbOsmFpbsSr90TP0YA xen/drivers/net/e1000/kcompat.c
+3fd76773I15-QqRK-uQAdEd-cBFVZw xen/drivers/net/e1000/kcompat.h
 3ddb79bfKvn9mt0kofpkw0QaWjxO6A xen/drivers/net/net_init.c
 3f0c428exbF4as5zi8GyGyDSUITmxg xen/drivers/net/pcnet32.c
 3ddb79bf_CBcu3QWYwq4bNAOnM2RqQ xen/drivers/net/setup.c
index 01ee6b98f3bf4ffaa9ebb70d84b4922bc85f691a..efcbf8419b9de6590df895760428dca7a058a6c2 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -50,7 +50,7 @@
 #include <linux/timer.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-//#include <linux/string.h>
+#include <linux/string.h>
 //#include <linux/pagemap.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 //#include <net/pkt_sched.h>
 #include <linux/list.h>
 #include <linux/reboot.h>
-#include <linux/tqueue.h>
+#ifdef NETIF_F_TSO
+#include <net/checksum.h>
+#endif
+#ifdef SIOCGMIIPHY
+#include <linux/mii.h>
+#endif
+#ifdef SIOCETHTOOL
 #include <linux/ethtool.h>
+#endif
+#ifdef NETIF_F_HW_VLAN_TX
 #include <linux/if_vlan.h>
+#endif
 
 #define BAR_0          0
 #define BAR_1          1
 #define PCI_DMA_64BIT  0xffffffffffffffffULL
 #define PCI_DMA_32BIT  0x00000000ffffffffULL
 
+#include "kcompat.h"
 
 struct e1000_adapter;
 
-// XEN XXX
-// #define DBG 1
-
 #include "e1000_hw.h"
 
 #if DBG
@@ -97,6 +104,15 @@ struct e1000_adapter;
 #define E1000_RXBUFFER_8192  8192
 #define E1000_RXBUFFER_16384 16384
 
+/* SmartSpeed delimiters */
+#define E1000_SMARTSPEED_DOWNSHIFT 3
+#define E1000_SMARTSPEED_MAX       15
+
+/* Packet Buffer allocations */
+#define E1000_TX_FIFO_SIZE_SHIFT 0xA
+#define E1000_TX_HEAD_ADDR_SHIFT 7
+#define E1000_PBA_TX_MASK 0xFFFF0000
+
 /* Flow Control High-Watermark: 43464 bytes */
 #define E1000_FC_HIGH_THRESH 0xA9C8
 
@@ -109,13 +125,15 @@ struct e1000_adapter;
 /* How many Tx Descriptors do we need to call netif_wake_queue ? */
 #define E1000_TX_QUEUE_WAKE    16
 /* How many Rx Buffers do we bundle into one write to the hardware ? */
-#define E1000_RX_BUFFER_WRITE  16
-
-#define E1000_JUMBO_PBA      0x00000028
-#define E1000_DEFAULT_PBA    0x00000030
+#define E1000_RX_BUFFER_WRITE  16      /* Must be power of 2 */
 
 #define AUTO_ALL_MODES       0
-#define E1000_EEPROM_APME    4
+#define E1000_EEPROM_APME    0x0400
+
+#ifndef E1000_MASTER_SLAVE
+/* Switch to override PHY master/slave setting */
+#define E1000_MASTER_SLAVE     e1000_ms_hw_default
+#endif
 
 /* only works for sizes that are powers of 2 */
 #define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
@@ -127,6 +145,7 @@ struct e1000_buffer {
        uint64_t dma;
        unsigned long length;
        unsigned long time_stamp;
+       unsigned int next_to_watch;
 };
 
 struct e1000_desc_ring {
@@ -147,7 +166,8 @@ struct e1000_desc_ring {
 };
 
 #define E1000_DESC_UNUSED(R) \
-((((R)->next_to_clean + (R)->count) - ((R)->next_to_use + 1)) % ((R)->count))
+       ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+       (R)->next_to_clean - (R)->next_to_use - 1)
 
 #define E1000_GET_DESC(R, i, type)     (&(((struct type *)((R).desc))[i]))
 #define E1000_RX_DESC(R, i)            E1000_GET_DESC(R, i, e1000_rx_desc)
@@ -157,29 +177,40 @@ struct e1000_desc_ring {
 /* board specific private data structure */
 
 struct e1000_adapter {
+       struct timer_list tx_fifo_stall_timer;
        struct timer_list watchdog_timer;
        struct timer_list phy_info_timer;
+#ifdef NETIF_F_HW_VLAN_TX
        struct vlan_group *vlgrp;
-       char *id_string;
+#endif
        uint32_t bd_number;
        uint32_t rx_buffer_len;
        uint32_t part_num;
        uint32_t wol;
+       uint32_t smartspeed;
        uint16_t link_speed;
        uint16_t link_duplex;
        spinlock_t stats_lock;
        atomic_t irq_sem;
-       struct tq_struct tx_timeout_task;
+       struct work_struct tx_timeout_task;
+       uint8_t fc_autoneg;
 
+#ifdef ETHTOOL_PHYS_ID
        struct timer_list blink_timer;
        unsigned long led_status;
+#endif
 
        /* TX */
        struct e1000_desc_ring tx_ring;
        uint32_t txd_cmd;
        uint32_t tx_int_delay;
        uint32_t tx_abs_int_delay;
-       int max_data_per_txd;
+       uint32_t gotcl;
+       uint32_t tx_fifo_head;
+       uint32_t tx_head_addr;
+       uint32_t tx_fifo_size;
+       atomic_t tx_fifo_stall;
+       boolean_t pcix_82544;
 
        /* RX */
        struct e1000_desc_ring rx_ring;
@@ -188,6 +219,10 @@ struct e1000_adapter {
        uint32_t rx_int_delay;
        uint32_t rx_abs_int_delay;
        boolean_t rx_csum;
+       uint32_t gorcl;
+
+       /* Interrupt Throttle Rate */
+       uint32_t itr;
 
        /* OS defined structs */
        struct net_device *netdev;
@@ -200,10 +235,25 @@ struct e1000_adapter {
        struct e1000_phy_info phy_info;
        struct e1000_phy_stats phy_stats;
 
-       uint32_t pci_state[16];
-       char ifname[IFNAMSIZ];
+#ifdef ETHTOOL_TEST
+       uint32_t test_icr;
+       struct e1000_desc_ring test_tx_ring;
+       struct e1000_desc_ring test_rx_ring;
+#endif
+
+#ifdef E1000_COUNT_ICR
+       uint64_t icr_txdw;
+       uint64_t icr_txqe;
+       uint64_t icr_lsc;
+       uint64_t icr_rxseq;
+       uint64_t icr_rxdmt;
+       uint64_t icr_rxo;
+       uint64_t icr_rxt;
+       uint64_t icr_mdac;
+       uint64_t icr_rxcfg;
+       uint64_t icr_gpi;
+#endif
 
-       /* All new definitions should go below this point! */
-       spinlock_t tx_lock;
+       uint32_t pci_state[16];
 };
 #endif /* _E1000_H_ */
index d06ef79c6e29d13cbf50c6d90d30eba8a41fc44f..fdfaa807331fec42bf449e62d904cab2121e2450 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -30,6 +30,7 @@
 
 #include "e1000.h"
 
+#ifdef SIOCETHTOOL
 #include <asm/uaccess.h>
 
 extern char e1000_driver_name[];
@@ -38,16 +39,69 @@ extern char e1000_driver_version[];
 extern int e1000_up(struct e1000_adapter *adapter);
 extern void e1000_down(struct e1000_adapter *adapter);
 extern void e1000_reset(struct e1000_adapter *adapter);
+extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
+
+#ifndef ETH_GSTRING_LEN
+#define ETH_GSTRING_LEN 32
+#endif
+#ifdef ETHTOOL_GSTATS
+struct e1000_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int stat_offset;
+};
 
-static char e1000_gstrings_stats[][ETH_GSTRING_LEN] = {
-       "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
-       "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
-       "rx_length_errors", "rx_over_errors", "rx_crc_errors",
-       "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
-       "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
-       "tx_heartbeat_errors", "tx_window_errors",
+#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \
+                     offsetof(struct e1000_adapter, m)
+static struct e1000_stats e1000_gstrings_stats[] = {
+       { "rx_packets", E1000_STAT(net_stats.rx_packets) },
+       { "tx_packets", E1000_STAT(net_stats.tx_packets) },
+       { "rx_bytes", E1000_STAT(net_stats.rx_bytes) },
+       { "tx_bytes", E1000_STAT(net_stats.tx_bytes) },
+       { "rx_errors", E1000_STAT(net_stats.rx_errors) },
+       { "tx_errors", E1000_STAT(net_stats.tx_errors) },
+       { "rx_dropped", E1000_STAT(net_stats.rx_dropped) },
+       { "tx_dropped", E1000_STAT(net_stats.tx_dropped) },
+       { "multicast", E1000_STAT(net_stats.multicast) },
+       { "collisions", E1000_STAT(net_stats.collisions) },
+       { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) },
+       { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) },
+       { "rx_crc_errors", E1000_STAT(net_stats.rx_crc_errors) },
+       { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) },
+       { "rx_fifo_errors", E1000_STAT(net_stats.rx_fifo_errors) },
+       { "rx_missed_errors", E1000_STAT(net_stats.rx_missed_errors) },
+       { "tx_aborted_errors", E1000_STAT(net_stats.tx_aborted_errors) },
+       { "tx_carrier_errors", E1000_STAT(net_stats.tx_carrier_errors) },
+       { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) },
+       { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) },
+       { "tx_window_errors", E1000_STAT(net_stats.tx_window_errors) },
+       { "tx_abort_late_coll", E1000_STAT(stats.latecol) },
+       { "tx_deferred_ok", E1000_STAT(stats.dc) },
+       { "tx_single_coll_ok", E1000_STAT(stats.scc) },
+       { "tx_multi_coll_ok", E1000_STAT(stats.mcc) },
+       { "rx_long_length_errors", E1000_STAT(stats.roc) },
+       { "rx_short_length_errors", E1000_STAT(stats.ruc) },
+       { "rx_align_errors", E1000_STAT(stats.algnerrc) },
+       { "tx_tcp_seg_good", E1000_STAT(stats.tsctc) },
+       { "tx_tcp_seg_failed", E1000_STAT(stats.tsctfc) },
+       { "rx_flow_control_xon", E1000_STAT(stats.xonrxc) },
+       { "rx_flow_control_xoff", E1000_STAT(stats.xoffrxc) },
+       { "tx_flow_control_xon", E1000_STAT(stats.xontxc) },
+       { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) },
+       { "rx_csum_offload_good", E1000_STAT(hw_csum_good) },
+       { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) }
+};
+#define E1000_STATS_LEN        \
+       sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats)
+#endif /* ETHTOOL_GSTATS */
+#ifdef ETHTOOL_TEST
+static char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
+       "Register test  (offline)", "Eeprom test    (offline)",
+       "Interrupt test (offline)", "Loopback test  (offline)",
+       "Link test   (on/offline)"
 };
-#define E1000_STATS_LEN        sizeof(e1000_gstrings_stats) / ETH_GSTRING_LEN
+#define E1000_TEST_LEN sizeof(e1000_gstrings_test) / ETH_GSTRING_LEN
+#endif /* ETHTOOL_TEST */
 
 static void
 e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
@@ -129,30 +183,9 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
                hw->autoneg = 1;
                hw->autoneg_advertised = 0x002F;
                ecmd->advertising = 0x002F;
-       } else {
-               hw->autoneg = 0;
-               switch(ecmd->speed + ecmd->duplex) {
-               case SPEED_10 + DUPLEX_HALF:
-                       hw->forced_speed_duplex = e1000_10_half;
-                       break;
-               case SPEED_10 + DUPLEX_FULL:
-                       hw->forced_speed_duplex = e1000_10_full;
-                       break;
-               case SPEED_100 + DUPLEX_HALF:
-                       hw->forced_speed_duplex = e1000_100_half;
-                       break;
-               case SPEED_100 + DUPLEX_FULL:
-                       hw->forced_speed_duplex = e1000_100_full;
-                       break;
-               case SPEED_1000 + DUPLEX_FULL:
-                       hw->autoneg = 1;
-                       hw->autoneg_advertised = ADVERTISE_1000_FULL;
-                       break;
-               case SPEED_1000 + DUPLEX_HALF: /* not supported */
-               default:
+       } else
+               if(e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex))
                        return -EINVAL;
-               }
-       }
 
        /* reset the link */
 
@@ -165,16 +198,60 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
        return 0;
 }
 
-static inline int
-e1000_eeprom_size(struct e1000_hw *hw)
+#ifdef ETHTOOL_GPAUSEPARAM
+static int
+e1000_ethtool_gpause(struct e1000_adapter *adapter,
+                     struct ethtool_pauseparam *epause)
 {
-       if((hw->mac_type > e1000_82544) &&
-          (E1000_READ_REG(hw, EECD) & E1000_EECD_SIZE))
-               return 512;
-       else
-               return 128;
+       struct e1000_hw *hw = &adapter->hw;
+       
+       epause->autoneg = 
+               (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
+       
+       if(hw->fc == e1000_fc_rx_pause)
+               epause->rx_pause = 1;
+       else if(hw->fc == e1000_fc_tx_pause)
+               epause->tx_pause = 1;
+       else if(hw->fc == e1000_fc_full) {
+               epause->rx_pause = 1;
+               epause->tx_pause = 1;
+       }
+       
+       return 0;
+}
+#endif /* ETHTOOL_GPAUSEPARAM */
+
+#ifdef ETHTOOL_SPAUSEPARAM
+static int
+e1000_ethtool_spause(struct e1000_adapter *adapter,
+                     struct ethtool_pauseparam *epause)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       
+       adapter->fc_autoneg = epause->autoneg;
+
+       if(epause->rx_pause && epause->tx_pause)
+               hw->fc = e1000_fc_full;
+       else if(epause->rx_pause && !epause->tx_pause)
+               hw->fc = e1000_fc_rx_pause;
+       else if(!epause->rx_pause && epause->tx_pause)
+               hw->fc = e1000_fc_tx_pause;
+       else if(!epause->rx_pause && !epause->tx_pause)
+               hw->fc = e1000_fc_none;
+
+       hw->original_fc = hw->fc;
+
+       if(netif_running(adapter->netdev)) {
+               e1000_down(adapter);
+               e1000_up(adapter);
+       } else
+               e1000_reset(adapter);
+       
+       return 0;
 }
+#endif /* ETHTOOL_SPAUSEPARAM */
 
+#ifdef ETHTOOL_GDRVINFO
 static void
 e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
                        struct ethtool_drvinfo *drvinfo)
@@ -183,17 +260,32 @@ e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
        strncpy(drvinfo->version, e1000_driver_version, 32);
        strncpy(drvinfo->fw_version, "N/A", 32);
        strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32);
+#ifdef ETHTOOL_GSTATS
        drvinfo->n_stats = E1000_STATS_LEN;
+#endif /* ETHTOOL_GSTATS */
+#ifdef ETHTOOL_TEST
+       drvinfo->testinfo_len = E1000_TEST_LEN;
+#endif /* ETHTOOL_TEST */
+#ifdef ETHTOOL_GEEPROM  /* GREGS broken in earlier ethtool.h */
+#ifdef ETHTOOL_GREGS
 #define E1000_REGS_LEN 32
        drvinfo->regdump_len  = E1000_REGS_LEN * sizeof(uint32_t);
-       drvinfo->eedump_len  = e1000_eeprom_size(&adapter->hw);
+#endif /* ETHTOOL_GREGS */
+#endif /* ETHTOOL_GEEPROM */
+#ifdef ETHTOOL_GEEPROM
+       drvinfo->eedump_len = adapter->hw.eeprom.word_size * 2;
+#endif /* ETHTOOL_GEEPROM */
 }
+#endif /* ETHTOOL_GDRVINFO */
 
+#ifdef ETHTOOL_GEEPROM  /* GREGS broken in earlier ethtool.h */
+#ifdef  ETHTOOL_GREGS
 static void
 e1000_ethtool_gregs(struct e1000_adapter *adapter,
                     struct ethtool_regs *regs, uint32_t *regs_buff)
 {
        struct e1000_hw *hw = &adapter->hw;
+       uint16_t phy_data;
 
        regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
 
@@ -212,17 +304,75 @@ e1000_ethtool_gregs(struct e1000_adapter *adapter,
        regs_buff[10] = E1000_READ_REG(hw, TDT);
        regs_buff[11] = E1000_READ_REG(hw, TIDV);
 
+       regs_buff[12] = adapter->hw.phy_type;  /* PHY type (IGP=1, M88=0) */
+       if(hw->phy_type == e1000_phy_igp) {
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                   IGP01E1000_PHY_AGC_A);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_A &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[13] = (uint32_t)phy_data; /* cable length */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                   IGP01E1000_PHY_AGC_B);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_B &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[14] = (uint32_t)phy_data; /* cable length */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                   IGP01E1000_PHY_AGC_C);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_C &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[15] = (uint32_t)phy_data; /* cable length */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                   IGP01E1000_PHY_AGC_D);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_D &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[16] = (uint32_t)phy_data; /* cable length */
+               regs_buff[17] = 0; /* extended 10bt distance (not needed) */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[18] = (uint32_t)phy_data; /* cable polarity */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                   IGP01E1000_PHY_PCS_INIT_REG);
+               e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG &
+                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data);
+               regs_buff[19] = (uint32_t)phy_data; /* cable polarity */
+               regs_buff[20] = 0; /* polarity correction enabled (always) */
+               regs_buff[22] = 0; /* phy receive errors (unavailable) */
+               regs_buff[23] = regs_buff[18]; /* mdix mode */
+               e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0);
+       } else {
+               e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
+               regs_buff[13] = (uint32_t)phy_data; /* cable length */
+               regs_buff[14] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
+               regs_buff[15] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
+               regs_buff[16] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
+               e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+               regs_buff[17] = (uint32_t)phy_data; /* extended 10bt distance */
+               regs_buff[18] = regs_buff[13]; /* cable polarity */
+               regs_buff[19] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
+               regs_buff[20] = regs_buff[17]; /* polarity correction */
+               /* phy receive errors */
+               regs_buff[22] = adapter->phy_stats.receive_errors;
+               regs_buff[23] = regs_buff[13]; /* mdix mode */
+       }
+       regs_buff[21] = adapter->phy_stats.idle_errors;  /* phy idle errors */
+       e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
+       regs_buff[24] = (uint32_t)phy_data;  /* phy local receiver status */
+       regs_buff[25] = regs_buff[24];  /* phy remote receiver status */
+
        return;
 }
+#endif  /* ETHTOOL_GREGS */
+#endif /* ETHTOOL_GEEPROM */
 
+#ifdef ETHTOOL_GEEPROM
 static int
 e1000_ethtool_geeprom(struct e1000_adapter *adapter,
                       struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff)
 {
        struct e1000_hw *hw = &adapter->hw;
-       int max_len, first_word, last_word;
+       int first_word, last_word;
        int ret_val = 0;
-       int i;
 
        if(eeprom->len == 0) {
                ret_val = -EINVAL;
@@ -231,35 +381,42 @@ e1000_ethtool_geeprom(struct e1000_adapter *adapter,
 
        eeprom->magic = hw->vendor_id | (hw->device_id << 16);
 
-       max_len = e1000_eeprom_size(hw);
-
        if(eeprom->offset > eeprom->offset + eeprom->len) {
                ret_val = -EINVAL;
                goto geeprom_error;
        }
 
-       if((eeprom->offset + eeprom->len) > max_len)
-               eeprom->len = (max_len - eeprom->offset);
+       if((eeprom->offset + eeprom->len) > (hw->eeprom.word_size * 2))
+               eeprom->len = ((hw->eeprom.word_size * 2) - eeprom->offset);
 
        first_word = eeprom->offset >> 1;
        last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 
-       for(i = 0; i <= (last_word - first_word); i++)
-               e1000_read_eeprom(hw, first_word + i, &eeprom_buff[i]);
-
+       if(hw->eeprom.type == e1000_eeprom_spi)
+               ret_val = e1000_read_eeprom(hw, first_word,
+                                           last_word - first_word + 1,
+                                           eeprom_buff);
+       else {
+               uint16_t i;
+               for (i = 0; i < last_word - first_word + 1; i++)
+                       if((ret_val = e1000_read_eeprom(hw, first_word + i, 1,
+                                                       &eeprom_buff[i])))
+                               break;
+       }
 geeprom_error:
        return ret_val;
 }
+#endif /* ETHTOOL_GEEPROM */
 
+#ifdef ETHTOOL_SEEPROM
 static int
 e1000_ethtool_seeprom(struct e1000_adapter *adapter,
                       struct ethtool_eeprom *eeprom, void *user_data)
 {
        struct e1000_hw *hw = &adapter->hw;
        uint16_t *eeprom_buff;
-       int max_len, first_word, last_word;
        void *ptr;
-       int i;
+       int max_len, first_word, last_word, ret_val = 0;
 
        if(eeprom->len == 0)
                return -EOPNOTSUPP;
@@ -267,7 +424,7 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
        if(eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
                return -EFAULT;
 
-       max_len = e1000_eeprom_size(hw);
+       max_len = hw->eeprom.word_size * 2;
 
        if((eeprom->offset + eeprom->len) > max_len)
                eeprom->len = (max_len - eeprom->offset);
@@ -275,7 +432,7 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
        first_word = eeprom->offset >> 1;
        last_word = (eeprom->offset + eeprom->len - 1) >> 1;
        eeprom_buff = kmalloc(max_len, GFP_KERNEL);
-       if(eeprom_buff == NULL)
+       if(!eeprom_buff)
                return -ENOMEM;
 
        ptr = (void *)eeprom_buff;
@@ -283,32 +440,807 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
        if(eeprom->offset & 1) {
                /* need read/modify/write of first changed EEPROM word */
                /* only the second byte of the word is being modified */
-               e1000_read_eeprom(hw, first_word, &eeprom_buff[0]);
+               ret_val = e1000_read_eeprom(hw, first_word, 1,
+                                           &eeprom_buff[0]);
                ptr++;
        }
-       if((eeprom->offset + eeprom->len) & 1) {
+       if(((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) {
                /* need read/modify/write of last changed EEPROM word */
                /* only the first byte of the word is being modified */
-               e1000_read_eeprom(hw, last_word,
+               ret_val = e1000_read_eeprom(hw, last_word, 1,
                                  &eeprom_buff[last_word - first_word]);
        }
-       if(copy_from_user(ptr, user_data, eeprom->len)) {
-               kfree(eeprom_buff);
-               return -EFAULT;
+       if((ret_val != 0) || copy_from_user(ptr, user_data, eeprom->len)) {
+               ret_val = -EFAULT;
+               goto seeprom_error;
        }
 
-       for(i = 0; i <= (last_word - first_word); i++)
-               e1000_write_eeprom(hw, first_word + i, eeprom_buff[i]);
+       ret_val = e1000_write_eeprom(hw, first_word,
+                                    last_word - first_word + 1, eeprom_buff);
 
        /* Update the checksum over the first part of the EEPROM if needed */
-       if(first_word <= EEPROM_CHECKSUM_REG)
+       if((ret_val == 0) && first_word <= EEPROM_CHECKSUM_REG)
                e1000_update_eeprom_checksum(hw);
 
+seeprom_error:
        kfree(eeprom_buff);
+       return ret_val;
+}
+#endif /* ETHTOOL_SEEPROM */
+
+#ifdef ETHTOOL_TEST
+#define REG_PATTERN_TEST(R, M, W)                                              \
+{                                                                              \
+       uint32_t pat, value;                                                   \
+       uint32_t test[] =                                                      \
+               {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};              \
+       for(pat = 0; pat < sizeof(test)/sizeof(test[0]); pat++) {              \
+               E1000_WRITE_REG(&adapter->hw, R, (test[pat] & W));             \
+               value = E1000_READ_REG(&adapter->hw, R);                       \
+               if(value != (test[pat] & W & M)) {                             \
+                       *data = (adapter->hw.mac_type < e1000_82543) ?         \
+                               E1000_82542_##R : E1000_##R;                   \
+                       return 1;                                              \
+               }                                                              \
+       }                                                                      \
+}
+
+#define REG_SET_AND_CHECK(R, M, W)                                             \
+{                                                                              \
+       uint32_t value;                                                        \
+       E1000_WRITE_REG(&adapter->hw, R, W & M);                               \
+       value = E1000_READ_REG(&adapter->hw, R);                               \
+       if ((W & M) != (value & M)) {                                          \
+               *data = (adapter->hw.mac_type < e1000_82543) ?                 \
+                       E1000_82542_##R : E1000_##R;                           \
+               return 1;                                                      \
+       }                                                                      \
+}
+
+static int
+e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data)
+{
+       uint32_t value;
+       uint32_t i;
+
+       /* The status register is Read Only, so a write should fail.
+        * Some bits that get toggled are ignored.
+        */
+       value = (E1000_READ_REG(&adapter->hw, STATUS) & (0xFFFFF833));
+       E1000_WRITE_REG(&adapter->hw, STATUS, (0xFFFFFFFF));
+       if(value != (E1000_READ_REG(&adapter->hw, STATUS) & (0xFFFFF833))) {
+               *data = 1;
+               return 1;
+       }
+
+       REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(RDTR, 0x0000FFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(RDBAH, 0xFFFFFFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(RDLEN, 0x000FFF80, 0x000FFFFF);
+       REG_PATTERN_TEST(RDH, 0x0000FFFF, 0x0000FFFF);
+       REG_PATTERN_TEST(RDT, 0x0000FFFF, 0x0000FFFF);
+       REG_PATTERN_TEST(FCRTH, 0x0000FFF8, 0x0000FFF8);
+       REG_PATTERN_TEST(FCTTV, 0x0000FFFF, 0x0000FFFF);
+       REG_PATTERN_TEST(TIPG, 0x3FFFFFFF, 0x3FFFFFFF);
+       REG_PATTERN_TEST(TDBAH, 0xFFFFFFFF, 0xFFFFFFFF);
+       REG_PATTERN_TEST(TDLEN, 0x000FFF80, 0x000FFFFF);
+
+       REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x00000000);
+       REG_SET_AND_CHECK(RCTL, 0x06DFB3FE, 0x003FFFFB);
+       REG_SET_AND_CHECK(TCTL, 0xFFFFFFFF, 0x00000000);
+
+       if(adapter->hw.mac_type >= e1000_82543) {
+
+               REG_SET_AND_CHECK(RCTL, 0x06DFB3FE, 0xFFFFFFFF);
+               REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF);
+               REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF);
+               REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF);
+               REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF);
+
+               for(i = 0; i < E1000_RAR_ENTRIES; i++) {
+                       REG_PATTERN_TEST(RA + ((i << 1) << 2), 0xFFFFFFFF,
+                                        0xFFFFFFFF);
+                       REG_PATTERN_TEST(RA + (((i << 1) + 1) << 2), 0x8003FFFF,
+                                        0xFFFFFFFF);
+               }
+
+       } else {
+
+               REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x01FFFFFF);
+               REG_PATTERN_TEST(RDBAL, 0xFFFFF000, 0xFFFFFFFF);
+               REG_PATTERN_TEST(TXCW, 0x0000FFFF, 0x0000FFFF);
+               REG_PATTERN_TEST(TDBAL, 0xFFFFF000, 0xFFFFFFFF);
+
+       }
+
+       for(i = 0; i < E1000_MC_TBL_SIZE; i++)
+               REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF);
+
+       return 0;
+}
+
+static int
+e1000_eeprom_test(struct e1000_adapter *adapter, uint64_t *data)
+{
+       uint16_t temp;
+       uint16_t checksum = 0;
+       uint16_t i;
+
+       *data = 0;
+       /* Read and add up the contents of the EEPROM */
+       for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
+               if((e1000_read_eeprom(&adapter->hw, i, 1, &temp)) < 0) {
+                       *data = 1;
+                       break;
+               }
+               checksum += temp;
+       }
+
+       /* If Checksum is not Correct return error else test passed */
+       if((checksum != (uint16_t) EEPROM_SUM) && !(*data))
+               *data = 2;
+
+       return *data;
+}
+
+static irqreturn_t
+e1000_test_intr(int irq,
+               void *data,
+               struct pt_regs *regs)
+{
+       struct net_device *netdev = (struct net_device *) data;
+       struct e1000_adapter *adapter = netdev->priv;
+
+       adapter->test_icr |= E1000_READ_REG(&adapter->hw, ICR);
+
+       return IRQ_HANDLED;
+}
+
+static int
+e1000_intr_test(struct e1000_adapter *adapter, uint64_t *data)
+{
+       struct net_device *netdev = adapter->netdev;
+       uint32_t icr, mask, i=0;
+
+       *data = 0;
+
+       /* Hook up test interrupt handler just for this test */
+       if(request_irq
+          (netdev->irq, &e1000_test_intr, SA_SHIRQ, netdev->name, netdev)) {
+               *data = 1;
+               return -1;
+       }
+
+       /* Disable all the interrupts */
+       E1000_WRITE_REG(&adapter->hw, IMC, 0xFFFFFFFF);
+       msec_delay(10);
+
+       /* Interrupts are disabled, so read interrupt cause
+        * register (icr) twice to verify that there are no interrupts
+        * pending.  icr is clear on read.
+        */
+       icr = E1000_READ_REG(&adapter->hw, ICR);
+       icr = E1000_READ_REG(&adapter->hw, ICR);
+
+       if(icr != 0) {
+               /* if icr is non-zero, there is no point
+                * running other interrupt tests.
+                */
+               *data = 2;
+               i = 10;
+       }
+
+       /* Test each interrupt */
+       for(; i < 10; i++) {
+
+               /* Interrupt to test */
+               mask = 1 << i;
+
+               /* Disable the interrupt to be reported in
+                * the cause register and then force the same
+                * interrupt and see if one gets posted.  If
+                * an interrupt was posted to the bus, the
+                * test failed.
+                */
+               adapter->test_icr = 0;
+               E1000_WRITE_REG(&adapter->hw, IMC, mask);
+               E1000_WRITE_REG(&adapter->hw, ICS, mask);
+               msec_delay(10);
+
+               if(adapter->test_icr & mask) {
+                       *data = 3;
+                       break;
+               }
+
+               /* Enable the interrupt to be reported in
+                * the cause register and then force the same
+                * interrupt and see if one gets posted.  If
+                * an interrupt was not posted to the bus, the
+                * test failed.
+                */
+               adapter->test_icr = 0;
+               E1000_WRITE_REG(&adapter->hw, IMS, mask);
+               E1000_WRITE_REG(&adapter->hw, ICS, mask);
+               msec_delay(10);
+
+               if(!(adapter->test_icr & mask)) {
+                       *data = 4;
+                       break;
+               }
+
+               /* Disable the other interrupts to be reported in
+                * the cause register and then force the other
+                * interrupts and see if any get posted.  If
+                * an interrupt was posted to the bus, the
+                * test failed.
+                */
+               adapter->test_icr = 0;
+               E1000_WRITE_REG(&adapter->hw, IMC, ~mask);
+               E1000_WRITE_REG(&adapter->hw, ICS, ~mask);
+               msec_delay(10);
+
+               if(adapter->test_icr) {
+                       *data = 5;
+                       break;
+               }
+       }
+
+       /* Disable all the interrupts */
+       E1000_WRITE_REG(&adapter->hw, IMC, 0xFFFFFFFF);
+       msec_delay(10);
+
+       /* Unhook test interrupt handler */
+       free_irq(netdev->irq, netdev);
+
+       return *data;
+}
+
+static void
+e1000_free_desc_rings(struct e1000_adapter *adapter)
+{
+       struct e1000_desc_ring *txdr = &adapter->test_tx_ring;
+       struct e1000_desc_ring *rxdr = &adapter->test_rx_ring;
+       struct pci_dev *pdev = adapter->pdev;
+       int i;
+
+       if(txdr->desc && txdr->buffer_info) {
+               for(i = 0; i < txdr->count; i++) {
+                       if(txdr->buffer_info[i].dma)
+                               pci_unmap_single(pdev, txdr->buffer_info[i].dma,
+                                                txdr->buffer_info[i].length,
+                                                PCI_DMA_TODEVICE);
+                       if(txdr->buffer_info[i].skb)
+                               dev_kfree_skb(txdr->buffer_info[i].skb);
+               }
+       }
+
+       if(rxdr->desc && rxdr->buffer_info) {
+               for(i = 0; i < rxdr->count; i++) {
+                       if(rxdr->buffer_info[i].dma)
+                               pci_unmap_single(pdev, rxdr->buffer_info[i].dma,
+                                                rxdr->buffer_info[i].length,
+                                                PCI_DMA_FROMDEVICE);
+                       if(rxdr->buffer_info[i].skb)
+                               dev_kfree_skb(rxdr->buffer_info[i].skb);
+               }
+       }
+
+       if(txdr->desc)
+               pci_free_consistent(pdev, txdr->size, txdr->desc, txdr->dma);
+       if(rxdr->desc)
+               pci_free_consistent(pdev, rxdr->size, rxdr->desc, rxdr->dma);
+
+       if(txdr->buffer_info)
+               kfree(txdr->buffer_info);
+       if(rxdr->buffer_info)
+               kfree(rxdr->buffer_info);
+
+       return;
+}
+
+static int
+e1000_setup_desc_rings(struct e1000_adapter *adapter)
+{
+       struct e1000_desc_ring *txdr = &adapter->test_tx_ring;
+       struct e1000_desc_ring *rxdr = &adapter->test_rx_ring;
+       struct pci_dev *pdev = adapter->pdev;
+       uint32_t rctl;
+       int size, i, ret_val;
+
+       /* Setup Tx descriptor ring and Tx buffers */
+
+       txdr->count = 80;
+
+       size = txdr->count * sizeof(struct e1000_buffer);
+       if(!(txdr->buffer_info = kmalloc(size, GFP_KERNEL))) {
+               ret_val = 1;
+               goto err_nomem;
+       }
+       memset(txdr->buffer_info, 0, size);
+
+       txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
+       E1000_ROUNDUP(txdr->size, 4096);
+       if(!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma))) {
+               ret_val = 2;
+               goto err_nomem;
+       }
+       memset(txdr->desc, 0, txdr->size);
+       txdr->next_to_use = txdr->next_to_clean = 0;
+
+       E1000_WRITE_REG(&adapter->hw, TDBAL,
+                       ((uint64_t) txdr->dma & 0x00000000FFFFFFFF));
+       E1000_WRITE_REG(&adapter->hw, TDBAH, ((uint64_t) txdr->dma >> 32));
+       E1000_WRITE_REG(&adapter->hw, TDLEN,
+                       txdr->count * sizeof(struct e1000_tx_desc));
+       E1000_WRITE_REG(&adapter->hw, TDH, 0);
+       E1000_WRITE_REG(&adapter->hw, TDT, 0);
+       E1000_WRITE_REG(&adapter->hw, TCTL,
+                       E1000_TCTL_PSP | E1000_TCTL_EN |
+                       E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT |
+                       E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT);
+
+       for(i = 0; i < txdr->count; i++) {
+               struct e1000_tx_desc *tx_desc = E1000_TX_DESC(*txdr, i);
+               struct sk_buff *skb;
+               unsigned int size = 1024;
+
+               if(!(skb = alloc_skb(size, GFP_KERNEL))) {
+                       ret_val = 3;
+                       goto err_nomem;
+               }
+               skb_put(skb, size);
+               txdr->buffer_info[i].skb = skb;
+               txdr->buffer_info[i].length = skb->len;
+               txdr->buffer_info[i].dma =
+                       pci_map_single(pdev, skb->data, skb->len,
+                                      PCI_DMA_TODEVICE);
+               tx_desc->buffer_addr = cpu_to_le64(txdr->buffer_info[i].dma);
+               tx_desc->lower.data = cpu_to_le32(skb->len);
+               tx_desc->lower.data |= E1000_TXD_CMD_EOP;
+               tx_desc->lower.data |= E1000_TXD_CMD_IFCS;
+               tx_desc->lower.data |= E1000_TXD_CMD_RPS;
+               tx_desc->upper.data = 0;
+       }
+
+       /* Setup Rx descriptor ring and Rx buffers */
+
+       rxdr->count = 80;
+
+       size = rxdr->count * sizeof(struct e1000_buffer);
+       if(!(rxdr->buffer_info = kmalloc(size, GFP_KERNEL))) {
+               ret_val = 4;
+               goto err_nomem;
+       }
+       memset(rxdr->buffer_info, 0, size);
+
+       rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc);
+       if(!(rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma))) {
+               ret_val = 5;
+               goto err_nomem;
+       }
+       memset(rxdr->desc, 0, rxdr->size);
+       rxdr->next_to_use = rxdr->next_to_clean = 0;
+
+       rctl = E1000_READ_REG(&adapter->hw, RCTL);
+       E1000_WRITE_REG(&adapter->hw, RCTL, rctl & ~E1000_RCTL_EN);
+       E1000_WRITE_REG(&adapter->hw, RDBAL,
+                       ((uint64_t) rxdr->dma & 0xFFFFFFFF));
+       E1000_WRITE_REG(&adapter->hw, RDBAH, ((uint64_t) rxdr->dma >> 32));
+       E1000_WRITE_REG(&adapter->hw, RDLEN, rxdr->size);
+       E1000_WRITE_REG(&adapter->hw, RDH, 0);
+       E1000_WRITE_REG(&adapter->hw, RDT, 0);
+       rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
+               E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+               (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT);
+       E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
+
+       for(i = 0; i < rxdr->count; i++) {
+               struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rxdr, i);
+               struct sk_buff *skb;
+
+               if(!(skb = alloc_skb(E1000_RXBUFFER_2048 + 2, GFP_KERNEL))) {
+                       ret_val = 6;
+                       goto err_nomem;
+               }
+               skb_reserve(skb, 2);
+               rxdr->buffer_info[i].skb = skb;
+               rxdr->buffer_info[i].length = E1000_RXBUFFER_2048;
+               rxdr->buffer_info[i].dma =
+                       pci_map_single(pdev, skb->data, E1000_RXBUFFER_2048,
+                                      PCI_DMA_FROMDEVICE);
+               rx_desc->buffer_addr = cpu_to_le64(rxdr->buffer_info[i].dma);
+               memset(skb->data, 0x00, skb->len);
+       }
+
+       return 0;
+
+      err_nomem:
+       e1000_free_desc_rings(adapter);
+       return ret_val;
+}
+
+static void
+e1000_phy_disable_receiver(struct e1000_adapter *adapter)
+{
+       /* Write out to PHY registers 29 and 30 to disable the Receiver. */
+       e1000_write_phy_reg(&adapter->hw, 29, 0x001F);
+       e1000_write_phy_reg(&adapter->hw, 30, 0x8FFC);
+       e1000_write_phy_reg(&adapter->hw, 29, 0x001A);
+       e1000_write_phy_reg(&adapter->hw, 30, 0x8FF0);
+
+       return;
+}
+
+static void
+e1000_phy_reset_clk_and_crs(struct e1000_adapter *adapter)
+{
+       uint16_t phy_reg;
+
+       /* Because we reset the PHY above, we need to re-force TX_CLK in the
+        * Extended PHY Specific Control Register to 25MHz clock.  This
+        * value defaults back to a 2.5MHz clock when the PHY is reset.
+        */
+       e1000_read_phy_reg(&adapter->hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg);
+       phy_reg |= M88E1000_EPSCR_TX_CLK_25;
+       e1000_write_phy_reg(&adapter->hw,
+               M88E1000_EXT_PHY_SPEC_CTRL, phy_reg);
+
+       /* In addition, because of the s/w reset above, we need to enable
+        * CRS on TX.  This must be set for both full and half duplex
+        * operation.
+        */
+       e1000_read_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, &phy_reg);
+       phy_reg |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+       e1000_write_phy_reg(&adapter->hw,
+               M88E1000_PHY_SPEC_CTRL, phy_reg);
+}
+
+static int
+e1000_nonintegrated_phy_loopback(struct e1000_adapter *adapter)
+{
+       uint32_t ctrl_reg;
+       uint16_t phy_reg;
+
+       /* Setup the Device Control Register for PHY loopback test. */
+
+       ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL);
+       ctrl_reg |= (E1000_CTRL_ILOS |          /* Invert Loss-Of-Signal */
+                    E1000_CTRL_FRCSPD |        /* Set the Force Speed Bit */
+                    E1000_CTRL_FRCDPX |        /* Set the Force Duplex Bit */
+                    E1000_CTRL_SPD_1000 |      /* Force Speed to 1000 */
+                    E1000_CTRL_FD);            /* Force Duplex to FULL */
+
+       E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg);
+
+       /* Read the PHY Specific Control Register (0x10) */
+       e1000_read_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, &phy_reg);
+
+       /* Clear Auto-Crossover bits in PHY Specific Control Register
+        * (bits 6:5).
+        */
+       phy_reg &= ~M88E1000_PSCR_AUTO_X_MODE;
+       e1000_write_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, phy_reg);
+
+       /* Perform software reset on the PHY */
+       e1000_phy_reset(&adapter->hw);
+
+       /* Have to setup TX_CLK and TX_CRS after software reset */
+       e1000_phy_reset_clk_and_crs(adapter);
+
+       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x8100);
+
+       /* Wait for reset to complete. */
+       usec_delay(500);
+
+       /* Have to setup TX_CLK and TX_CRS after software reset */
+       e1000_phy_reset_clk_and_crs(adapter);
+
+       /* Write out to PHY registers 29 and 30 to disable the Receiver. */
+       e1000_phy_disable_receiver(adapter);
+
+       /* Set the loopback bit in the PHY control register. */
+       e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
+       phy_reg |= MII_CR_LOOPBACK;
+       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
+
+       /* Setup TX_CLK and TX_CRS one more time. */
+       e1000_phy_reset_clk_and_crs(adapter);
+
+       /* Check Phy Configuration */
+       e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
+       if(phy_reg != 0x4100)
+                return 9;
+
+       e1000_read_phy_reg(&adapter->hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg);
+       if(phy_reg != 0x0070)
+               return 10;
+
+       e1000_read_phy_reg(&adapter->hw, 29, &phy_reg);
+       if(phy_reg != 0x001A)
+               return 11;
 
        return 0;
 }
 
+static int
+e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
+{
+       uint32_t ctrl_reg = 0;
+       uint32_t stat_reg = 0;
+
+       adapter->hw.autoneg = FALSE;
+
+       if(adapter->hw.phy_type == e1000_phy_m88) {
+               /* Auto-MDI/MDIX Off */
+               e1000_write_phy_reg(&adapter->hw,
+                                   M88E1000_PHY_SPEC_CTRL, 0x0808);
+               /* reset to update Auto-MDI/MDIX */
+               e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x9140);
+               /* autoneg off */
+               e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x8140);
+       }
+       /* force 1000, set loopback */
+       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x4140);
+
+       /* Now set up the MAC to the same speed/duplex as the PHY. */
+       ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL);
+       ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */
+       ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */
+                    E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */
+                    E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */
+                    E1000_CTRL_FD);     /* Force Duplex to FULL */
+
+       if(adapter->hw.media_type == e1000_media_type_copper &&
+          adapter->hw.phy_type == e1000_phy_m88) {
+               ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */
+       } else {
+               /* Set the ILOS bit on the fiber Nic is half
+                * duplex link is detected. */
+               stat_reg = E1000_READ_REG(&adapter->hw, STATUS);
+               if((stat_reg & E1000_STATUS_FD) == 0)
+                       ctrl_reg |= (E1000_CTRL_ILOS | E1000_CTRL_SLU);
+       }
+
+       E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg);
+
+       /* Disable the receiver on the PHY so when a cable is plugged in, the
+        * PHY does not begin to autoneg when a cable is reconnected to the NIC.
+        */
+       if(adapter->hw.phy_type == e1000_phy_m88)
+               e1000_phy_disable_receiver(adapter);
+
+       usec_delay(500);
+
+       return 0;
+}
+
+static int
+e1000_set_phy_loopback(struct e1000_adapter *adapter)
+{
+       uint16_t phy_reg = 0;
+       uint16_t count = 0;
+
+       switch (adapter->hw.mac_type) {
+       case e1000_82543:
+               if(adapter->hw.media_type == e1000_media_type_copper) {
+                       /* Attempt to setup Loopback mode on Non-integrated PHY.
+                        * Some PHY registers get corrupted at random, so
+                        * attempt this 10 times.
+                        */
+                       while(e1000_nonintegrated_phy_loopback(adapter) &&
+                             count++ < 10);
+                       if(count < 11)
+                               return 0;
+               }
+               break;
+
+       case e1000_82544:
+       case e1000_82540:
+       case e1000_82545:
+       case e1000_82545_rev_3:
+       case e1000_82546:
+       case e1000_82546_rev_3:
+       case e1000_82541:
+       case e1000_82541_rev_2:
+       case e1000_82547:
+       case e1000_82547_rev_2:
+               return e1000_integrated_phy_loopback(adapter);
+               break;
+
+       default:
+               /* Default PHY loopback work is to read the MII
+                * control register and assert bit 14 (loopback mode).
+                */
+               e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
+               phy_reg |= MII_CR_LOOPBACK;
+               e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
+               return 0;
+               break;
+       }
+
+       return 8;
+}
+
+static int
+e1000_setup_loopback_test(struct e1000_adapter *adapter)
+{
+       uint32_t rctl;
+
+       if(adapter->hw.media_type == e1000_media_type_fiber ||
+          adapter->hw.media_type == e1000_media_type_internal_serdes) {
+               if(adapter->hw.mac_type == e1000_82545 ||
+                  adapter->hw.mac_type == e1000_82546 ||
+                  adapter->hw.mac_type == e1000_82545_rev_3 ||
+                  adapter->hw.mac_type == e1000_82546_rev_3)
+                       return e1000_set_phy_loopback(adapter);
+               else {
+                       rctl = E1000_READ_REG(&adapter->hw, RCTL);
+                       rctl |= E1000_RCTL_LBM_TCVR;
+                       E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
+                       return 0;
+               }
+       } else if(adapter->hw.media_type == e1000_media_type_copper)
+               return e1000_set_phy_loopback(adapter);
+
+       return 7;
+}
+
+static void
+e1000_loopback_cleanup(struct e1000_adapter *adapter)
+{
+       uint32_t rctl;
+       uint16_t phy_reg;
+
+       rctl = E1000_READ_REG(&adapter->hw, RCTL);
+       rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
+       E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
+
+       if(adapter->hw.media_type == e1000_media_type_copper ||
+          ((adapter->hw.media_type == e1000_media_type_fiber ||
+            adapter->hw.media_type == e1000_media_type_internal_serdes) &&
+           (adapter->hw.mac_type == e1000_82545 ||
+            adapter->hw.mac_type == e1000_82546 ||
+            adapter->hw.mac_type == e1000_82545_rev_3 ||
+            adapter->hw.mac_type == e1000_82546_rev_3))) {
+               adapter->hw.autoneg = TRUE;
+               e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
+               if(phy_reg & MII_CR_LOOPBACK) {
+                       phy_reg &= ~MII_CR_LOOPBACK;
+                       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
+                       e1000_phy_reset(&adapter->hw);
+               }
+       }
+}
+
+static void
+e1000_create_lbtest_frame(struct sk_buff *skb, unsigned int frame_size)
+{
+       memset(skb->data, 0xFF, frame_size);
+       frame_size = (frame_size % 2) ? (frame_size - 1) : frame_size;
+       memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
+       memset(&skb->data[frame_size / 2 + 10], 0xBE, 1);
+       memset(&skb->data[frame_size / 2 + 12], 0xAF, 1);
+}
+
+static int
+e1000_check_lbtest_frame(struct sk_buff *skb, unsigned int frame_size)
+{
+       frame_size = (frame_size % 2) ? (frame_size - 1) : frame_size;
+       if(*(skb->data + 3) == 0xFF) {
+               if((*(skb->data + frame_size / 2 + 10) == 0xBE) &&
+                  (*(skb->data + frame_size / 2 + 12) == 0xAF)) {
+                       return 0;
+               }
+       }
+       return 13;
+}
+
+static int
+e1000_run_loopback_test(struct e1000_adapter *adapter)
+{
+       struct e1000_desc_ring *txdr = &adapter->test_tx_ring;
+       struct e1000_desc_ring *rxdr = &adapter->test_rx_ring;
+       struct pci_dev *pdev = adapter->pdev;
+       int i;
+
+       E1000_WRITE_REG(&adapter->hw, RDT, rxdr->count - 1);
+
+       for(i = 0; i < 64; i++) {
+               e1000_create_lbtest_frame(txdr->buffer_info[i].skb, 1024);
+               pci_dma_sync_single(pdev, txdr->buffer_info[i].dma,
+                                   txdr->buffer_info[i].length,
+                                   PCI_DMA_TODEVICE);
+       }
+       E1000_WRITE_REG(&adapter->hw, TDT, i);
+
+       msec_delay(200);
+
+       pci_dma_sync_single(pdev, rxdr->buffer_info[0].dma,
+                           rxdr->buffer_info[0].length, PCI_DMA_FROMDEVICE);
+
+       return e1000_check_lbtest_frame(rxdr->buffer_info[0].skb, 1024);
+}
+
+static int
+e1000_loopback_test(struct e1000_adapter *adapter, uint64_t *data)
+{
+       if((*data = e1000_setup_desc_rings(adapter))) goto err_loopback;
+       if((*data = e1000_setup_loopback_test(adapter))) goto err_loopback;
+       *data = e1000_run_loopback_test(adapter);
+       e1000_loopback_cleanup(adapter);
+       e1000_free_desc_rings(adapter);
+err_loopback:
+       return *data;
+}
+
+static int
+e1000_link_test(struct e1000_adapter *adapter, uint64_t *data)
+{
+       *data = 0;
+       e1000_check_for_link(&adapter->hw);
+
+       if(!(E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU)) {
+               *data = 1;
+       }
+       return *data;
+}
+
+static int
+e1000_ethtool_test(struct e1000_adapter *adapter,
+                  struct ethtool_test *eth_test, uint64_t *data)
+{
+       boolean_t if_running = netif_running(adapter->netdev);
+
+       if(eth_test->flags == ETH_TEST_FL_OFFLINE) {
+               /* Offline tests */
+
+               /* Link test performed before hardware reset so autoneg doesn't
+                * interfere with test result */
+               if(e1000_link_test(adapter, &data[4]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               if(if_running)
+                       e1000_down(adapter);
+               else
+                       e1000_reset(adapter);
+
+               if(e1000_reg_test(adapter, &data[0]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               e1000_reset(adapter);
+               if(e1000_eeprom_test(adapter, &data[1]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               e1000_reset(adapter);
+               if(e1000_intr_test(adapter, &data[2]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               e1000_reset(adapter);
+               if(e1000_loopback_test(adapter, &data[3]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               e1000_reset(adapter);
+               if(if_running)
+                       e1000_up(adapter);
+       } else {
+               /* Online tests */
+               if(e1000_link_test(adapter, &data[4]))
+                       eth_test->flags |= ETH_TEST_FL_FAILED;
+
+               /* Offline tests aren't run; pass by default */
+               data[0] = 0;
+               data[1] = 0;
+               data[2] = 0;
+               data[3] = 0;
+       }
+       return 0;
+}
+#endif /* ETHTOOL_TEST */
+
+#ifdef ETHTOOL_GWOL
 static void
 e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
 {
@@ -324,6 +1256,7 @@ e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
                return;
 
        case E1000_DEV_ID_82546EB_FIBER:
+       case E1000_DEV_ID_82546GB_FIBER:
                /* Wake events only supported on port A for dual fiber */
                if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) {
                        wol->supported = 0;
@@ -333,8 +1266,8 @@ e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
                /* Fall Through */
 
        default:
-               wol->supported = WAKE_UCAST | WAKE_MCAST
-                                WAKE_BCAST | WAKE_MAGIC;
+               wol->supported = WAKE_UCAST | WAKE_MCAST |
+                                WAKE_BCAST | WAKE_MAGIC;
 
                wol->wolopts = 0;
                if(adapter->wol & E1000_WUFC_EX)
@@ -348,7 +1281,9 @@ e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
                return;
        }
 }
+#endif /* ETHTOOL_GWOL */
 
+#ifdef ETHTOOL_SWOL
 static int
 e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
 {
@@ -362,13 +1297,14 @@ e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
                return wol->wolopts ? -EOPNOTSUPP : 0;
 
        case E1000_DEV_ID_82546EB_FIBER:
+       case E1000_DEV_ID_82546GB_FIBER:
                /* Wake events only supported on port A for dual fiber */
                if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
                        return wol->wolopts ? -EOPNOTSUPP : 0;
                /* Fall Through */
 
        default:
-               if(wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY))
+               if(wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
                        return -EOPNOTSUPP;
 
                adapter->wol = 0;
@@ -385,7 +1321,9 @@ e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
 
        return 0;
 }
+#endif /* ETHTOOL_SWOL */
 
+#ifdef ETHTOOL_PHYS_ID
 
 /* toggle LED 4 times per second = 2 "blinks" per second */
 #define E1000_ID_INTERVAL      (HZ/4)
@@ -431,6 +1369,7 @@ e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id)
 
        return 0;
 }
+#endif /* ETHTOOL_PHYS_ID */
 
 int
 e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
@@ -452,12 +1391,11 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
        }
        case ETHTOOL_SSET: {
                struct ethtool_cmd ecmd;
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
                if(copy_from_user(&ecmd, addr, sizeof(ecmd)))
                        return -EFAULT;
                return e1000_ethtool_sset(adapter, &ecmd);
        }
+#ifdef ETHTOOL_GDRVINFO
        case ETHTOOL_GDRVINFO: {
                struct ethtool_drvinfo drvinfo = {ETHTOOL_GDRVINFO};
                e1000_ethtool_gdrvinfo(adapter, &drvinfo);
@@ -465,28 +1403,59 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
                        return -EFAULT;
                return 0;
        }
+#endif /* ETHTOOL_GDRVINFO */
+#ifdef ETHTOOL_GSTRINGS
        case ETHTOOL_GSTRINGS: {
                struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS };
                char *strings = NULL;
+               int err = 0;
 
                if(copy_from_user(&gstrings, addr, sizeof(gstrings)))
                        return -EFAULT;
                switch(gstrings.string_set) {
-               case ETH_SS_STATS:
+#ifdef ETHTOOL_TEST
+               case ETH_SS_TEST:
+                       gstrings.len = E1000_TEST_LEN;
+                       strings = kmalloc(E1000_TEST_LEN * ETH_GSTRING_LEN,
+                                         GFP_KERNEL);
+                       if(!strings)
+                               return -ENOMEM;
+                       memcpy(strings, e1000_gstrings_test, E1000_TEST_LEN *
+                              ETH_GSTRING_LEN);
+                       break;
+#endif /* ETHTOOL_TEST */
+#ifdef ETHTOOL_GSTATS
+               case ETH_SS_STATS: {
+                       int i;
                        gstrings.len = E1000_STATS_LEN;
-                       strings = *e1000_gstrings_stats;
+                       strings = kmalloc(E1000_STATS_LEN * ETH_GSTRING_LEN,
+                                         GFP_KERNEL);
+                       if(!strings)
+                               return -ENOMEM;
+                       for(i=0; i < E1000_STATS_LEN; i++) {
+                               memcpy(&strings[i * ETH_GSTRING_LEN],
+                                      e1000_gstrings_stats[i].stat_string,
+                                      ETH_GSTRING_LEN);
+                       }
                        break;
+               }
+#endif /* ETHTOOL_GSTATS */
                default:
                        return -EOPNOTSUPP;
                }
                if(copy_to_user(addr, &gstrings, sizeof(gstrings)))
-                       return -EFAULT;
+                       err = -EFAULT;
                addr += offsetof(struct ethtool_gstrings, data);
-               if(copy_to_user(addr, strings,
+               if(!err && copy_to_user(addr, strings,
                   gstrings.len * ETH_GSTRING_LEN))
-                       return -EFAULT;
-               return 0;
+                       err = -EFAULT;
+
+               kfree(strings);
+               return err;
        }
+#endif /* ETHTOOL_GSTRINGS */
+#ifdef ETHTOOL_GEEPROM  /* GREGS broken in earlier ethtool.h */
+#ifdef  ETHTOOL_GREGS
        case ETHTOOL_GREGS: {
                struct ethtool_regs regs = {ETHTOOL_GREGS};
                uint32_t regs_buff[E1000_REGS_LEN];
@@ -503,21 +1472,26 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
 
                return 0;
        }
+#endif  /* ETHTOOL_GREGS */
+#endif  /* ETHTOOL_GEEPROM */
+#ifdef ETHTOOL_NWAY_RST
        case ETHTOOL_NWAY_RST: {
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
                if(netif_running(netdev)) {
                        e1000_down(adapter);
                        e1000_up(adapter);
                }
                return 0;
        }
+#endif /* ETHTOOL_NWAY_RST */
+#ifdef ETHTOOL_PHYS_ID
        case ETHTOOL_PHYS_ID: {
                struct ethtool_value id;
                if(copy_from_user(&id, addr, sizeof(id)))
                        return -EFAULT;
                return e1000_ethtool_led_blink(adapter, &id);
        }
+#endif /* ETHTOOL_PHYS_ID */
+#ifdef ETHTOOL_GLINK
        case ETHTOOL_GLINK: {
                struct ethtool_value link = {ETHTOOL_GLINK};
                link.data = netif_carrier_ok(netdev);
@@ -525,6 +1499,8 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
                        return -EFAULT;
                return 0;
        }
+#endif /* ETHTOOL_GLINK */
+#ifdef ETHTOOL_GWOL
        case ETHTOOL_GWOL: {
                struct ethtool_wolinfo wol = {ETHTOOL_GWOL};
                e1000_ethtool_gwol(adapter, &wol);
@@ -532,32 +1508,31 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
                        return -EFAULT;
                return 0;
        }
+#endif /* ETHTOOL_GWOL */
+#ifdef ETHTOOL_SWOL
        case ETHTOOL_SWOL: {
                struct ethtool_wolinfo wol;
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
                if(copy_from_user(&wol, addr, sizeof(wol)) != 0)
                        return -EFAULT;
                return e1000_ethtool_swol(adapter, &wol);
        }
+#endif /* ETHTOOL_SWOL */
+#ifdef ETHTOOL_GEEPROM
        case ETHTOOL_GEEPROM: {
                struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM};
+               struct e1000_hw *hw = &adapter->hw;
                uint16_t *eeprom_buff;
                void *ptr;
-               int max_len, err = 0;
+               int err = 0;
 
-               max_len = e1000_eeprom_size(&adapter->hw);
+               if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
+                       return -EFAULT;
 
-               eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+               eeprom_buff = kmalloc(hw->eeprom.word_size * 2, GFP_KERNEL);
 
-               if(eeprom_buff == NULL)
+               if(!eeprom_buff)
                        return -ENOMEM;
 
-               if(copy_from_user(&eeprom, addr, sizeof(eeprom))) {
-                       err = -EFAULT;
-                       goto err_geeprom_ioctl;
-               }
-
                if((err = e1000_ethtool_geeprom(adapter, &eeprom,
                                                eeprom_buff)))
                        goto err_geeprom_ioctl;
@@ -577,35 +1552,198 @@ err_geeprom_ioctl:
                kfree(eeprom_buff);
                return err;
        }
+#endif /* ETHTOOL_GEEPROM */
+#ifdef ETHTOOL_SEEPROM
        case ETHTOOL_SEEPROM: {
                struct ethtool_eeprom eeprom;
 
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-
                if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
                        return -EFAULT;
 
                addr += offsetof(struct ethtool_eeprom, data);
                return e1000_ethtool_seeprom(adapter, &eeprom, addr);
        }
+#endif /* ETHTOOL_SEEPROM */
+#ifdef ETHTOOL_GPAUSEPARAM
+       case ETHTOOL_GPAUSEPARAM: {
+               struct ethtool_pauseparam epause = {ETHTOOL_GPAUSEPARAM};
+               e1000_ethtool_gpause(adapter, &epause);
+               if(copy_to_user(addr, &epause, sizeof(epause)))
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_GPAUSEPARAM */
+#ifdef ETHTOOL_SPAUSEPARAM
+       case ETHTOOL_SPAUSEPARAM: {
+               struct ethtool_pauseparam epause;
+               if(copy_from_user(&epause, addr, sizeof(epause)))
+                       return -EFAULT;
+               return e1000_ethtool_spause(adapter, &epause);
+       }
+#endif /* ETHTOOL_SPAUSEPARAM */
+#ifdef ETHTOOL_GSTATS
        case ETHTOOL_GSTATS: {
                struct {
-                       struct ethtool_stats cmd;
+                       struct ethtool_stats eth_stats;
                        uint64_t data[E1000_STATS_LEN];
                } stats = { {ETHTOOL_GSTATS, E1000_STATS_LEN} };
                int i;
 
                for(i = 0; i < E1000_STATS_LEN; i++)
-                       stats.data[i] =
-                               ((unsigned long *)&adapter->net_stats)[i];
+                       stats.data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
+                                       sizeof(uint64_t)) ?
+                               *(uint64_t *)((char *)adapter +
+                                       e1000_gstrings_stats[i].stat_offset) :
+                               *(uint32_t *)((char *)adapter +
+                                       e1000_gstrings_stats[i].stat_offset);
                if(copy_to_user(addr, &stats, sizeof(stats)))
                        return -EFAULT;
                return 0;
        }
+#endif /* ETHTOOL_GSTATS */
+#ifdef ETHTOOL_TEST
+       case ETHTOOL_TEST: {
+               struct {
+                       struct ethtool_test eth_test;
+                       uint64_t data[E1000_TEST_LEN];
+               } test = { {ETHTOOL_TEST} };
+               int err;
+
+               if(copy_from_user(&test.eth_test, addr, sizeof(test.eth_test)))
+                       return -EFAULT;
+
+               test.eth_test.len = E1000_TEST_LEN;
+
+               if((err = e1000_ethtool_test(adapter, &test.eth_test,
+                                            test.data)))
+                       return err;
+
+               if(copy_to_user(addr, &test, sizeof(test)) != 0)
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_TEST */
+#ifdef ETHTOOL_GRXCSUM
+       case ETHTOOL_GRXCSUM: {
+               struct ethtool_value edata = { ETHTOOL_GRXCSUM };
+
+               edata.data = adapter->rx_csum;
+               if (copy_to_user(addr, &edata, sizeof(edata)))
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_GRXCSUM */
+#ifdef ETHTOOL_SRXCSUM
+       case ETHTOOL_SRXCSUM: {
+               struct ethtool_value edata;
+
+               if (copy_from_user(&edata, addr, sizeof(edata)))
+                       return -EFAULT;
+               adapter->rx_csum = edata.data;
+               if(netif_running(netdev)) {
+                       e1000_down(adapter);
+                       e1000_up(adapter);
+               } else
+                       e1000_reset(adapter);
+               return 0;
+       }
+#endif /* ETHTOOL_SRXCSUM */
+#ifdef ETHTOOL_GTXCSUM
+       case ETHTOOL_GTXCSUM: {
+               struct ethtool_value edata = { ETHTOOL_GTXCSUM };
+
+               edata.data =
+                       (netdev->features & NETIF_F_HW_CSUM) != 0;
+               if (copy_to_user(addr, &edata, sizeof(edata)))
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_GTXCSUM */
+#ifdef ETHTOOL_STXCSUM
+       case ETHTOOL_STXCSUM: {
+               struct ethtool_value edata;
+
+               if (copy_from_user(&edata, addr, sizeof(edata)))
+                       return -EFAULT;
+
+               if(adapter->hw.mac_type < e1000_82543) {
+                       if (edata.data != 0)
+                               return -EINVAL;
+                       return 0;
+               }
+
+               if (edata.data)
+                       netdev->features |= NETIF_F_HW_CSUM;
+               else
+                       netdev->features &= ~NETIF_F_HW_CSUM;
+
+               return 0;
+       }
+#endif /* ETHTOOL_STXCSUM */
+#ifdef ETHTOOL_GSG
+       case ETHTOOL_GSG: {
+               struct ethtool_value edata = { ETHTOOL_GSG };
+
+               edata.data =
+                       (netdev->features & NETIF_F_SG) != 0;
+               if (copy_to_user(addr, &edata, sizeof(edata)))
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_GSG */
+#ifdef ETHTOOL_SSG
+       case ETHTOOL_SSG: {
+               struct ethtool_value edata;
+
+               if (copy_from_user(&edata, addr, sizeof(edata)))
+                       return -EFAULT;
+
+               if (edata.data)
+                       netdev->features |= NETIF_F_SG;
+               else
+                       netdev->features &= ~NETIF_F_SG;
+
+               return 0;
+       }
+#endif /* ETHTOOL_SSG */
+#ifdef NETIF_F_TSO
+#ifdef ETHTOOL_GTSO
+       case ETHTOOL_GTSO: {
+               struct ethtool_value edata = { ETHTOOL_GTSO };
+
+               edata.data = (netdev->features & NETIF_F_TSO) != 0;
+               if (copy_to_user(addr, &edata, sizeof(edata)))
+                       return -EFAULT;
+               return 0;
+       }
+#endif /* ETHTOOL_GTSO */
+#ifdef ETHTOOL_STSO
+       case ETHTOOL_STSO: {
+               struct ethtool_value edata;
+
+               if (copy_from_user(&edata, addr, sizeof(edata)))
+                       return -EFAULT;
+
+               if ((adapter->hw.mac_type < e1000_82544) ||
+                   (adapter->hw.mac_type == e1000_82547)) {
+                       if (edata.data != 0)
+                               return -EINVAL;
+                       return 0;
+               }
+
+               if (edata.data)
+                       netdev->features |= NETIF_F_TSO;
+               else
+                       netdev->features &= ~NETIF_F_TSO;
+
+               return 0;
+       }
+#endif /* ETHTOOL_STSO */
+#endif
        default:
                return -EOPNOTSUPP;
        }
 }
 
+#endif /* SIOCETHTOOL */
 
index 1d70dab9371dc6d0a23d7c67a6332ca7d205f9fb..303651e11525ade0583e52d6d468d0e9c94d17c8 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
 
 #include "e1000_hw.h"
 
-static int32_t e1000_setup_fiber_link(struct e1000_hw *hw);
+static int32_t e1000_set_phy_type(struct e1000_hw *hw);
+static void e1000_phy_init_script(struct e1000_hw *hw);
 static int32_t e1000_setup_copper_link(struct e1000_hw *hw);
+static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw);
+static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw);
 static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw);
 static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw);
-static int32_t e1000_force_mac_fc(struct e1000_hw *hw);
 static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
 static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
-static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count);
+static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data,
+                                     uint16_t count);
 static uint16_t e1000_shift_in_mdi_bits(struct e1000_hw *hw);
 static int32_t e1000_phy_reset_dsp(struct e1000_hw *hw);
+static int32_t e1000_write_eeprom_spi(struct e1000_hw *hw, uint16_t offset,
+                                      uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_microwire(struct e1000_hw *hw,
+                                            uint16_t offset, uint16_t words,
+                                            uint16_t *data);
+static int32_t e1000_spi_eeprom_ready(struct e1000_hw *hw);
 static void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
 static void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
-static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, uint16_t count);
-static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw);
-static void e1000_setup_eeprom(struct e1000_hw *hw);
-static void e1000_clock_eeprom(struct e1000_hw *hw);
-static void e1000_cleanup_eeprom(struct e1000_hw *hw);
+static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data,
+                                    uint16_t count);
+static int32_t e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+                                      uint16_t phy_data);
+static int32_t e1000_read_phy_reg_ex(struct e1000_hw *hw,uint32_t reg_addr,
+                                     uint16_t *phy_data);
+static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count);
+static int32_t e1000_acquire_eeprom(struct e1000_hw *hw);
+static void e1000_release_eeprom(struct e1000_hw *hw);
 static void e1000_standby_eeprom(struct e1000_hw *hw);
 static int32_t e1000_id_led_init(struct e1000_hw * hw);
+static int32_t e1000_set_vco_speed(struct e1000_hw *hw);
+
+/* IGP cable length table */
+static const
+uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] =
+    { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+      5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25,
+      25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40,
+      40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60,
+      60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90,
+      90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+      100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+      110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120};
+
+
+/******************************************************************************
+ * Set the phy type member in the hw struct.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_set_phy_type(struct e1000_hw *hw)
+{
+    DEBUGFUNC("e1000_set_phy_type");
+
+    switch(hw->phy_id) {
+    case M88E1000_E_PHY_ID:
+    case M88E1000_I_PHY_ID:
+    case M88E1011_I_PHY_ID:
+        hw->phy_type = e1000_phy_m88;
+        break;
+    case IGP01E1000_I_PHY_ID:
+        hw->phy_type = e1000_phy_igp;
+        break;
+    default:
+        /* Should never have loaded on this device */
+        hw->phy_type = e1000_phy_undefined;
+        return -E1000_ERR_PHY_TYPE;
+    }
+
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * IGP phy init script - initializes the GbE PHY
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_phy_init_script(struct e1000_hw *hw)
+{
+    DEBUGFUNC("e1000_phy_init_script");
+
+    if(hw->phy_init_script) {
+        msec_delay(20);
+
+        e1000_write_phy_reg(hw,0x0000,0x0140);
+
+        msec_delay(5);
+
+        if(hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547) {
+            e1000_write_phy_reg(hw, 0x1F95, 0x0001);
+
+            e1000_write_phy_reg(hw, 0x1F71, 0xBD21);
+
+            e1000_write_phy_reg(hw, 0x1F79, 0x0018);
+
+            e1000_write_phy_reg(hw, 0x1F30, 0x1600);
+
+            e1000_write_phy_reg(hw, 0x1F31, 0x0014);
+
+            e1000_write_phy_reg(hw, 0x1F32, 0x161C);
+
+            e1000_write_phy_reg(hw, 0x1F94, 0x0003);
+
+            e1000_write_phy_reg(hw, 0x1F96, 0x003F);
+
+            e1000_write_phy_reg(hw, 0x2010, 0x0008);
+        } else {
+            e1000_write_phy_reg(hw, 0x1F73, 0x0099);
+        }
+
+        e1000_write_phy_reg(hw, 0x0000, 0x3300);
+
+
+        if(hw->mac_type == e1000_82547) {
+            uint16_t fused, fine, coarse;
+
+            /* Move to analog registers page */
+            e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused);
+
+            if(!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) {
+                e1000_read_phy_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS, &fused);
+
+                fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK;
+                coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK;
+
+                if(coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) {
+                    coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10;
+                    fine -= IGP01E1000_ANALOG_FUSE_FINE_1;
+                } else if(coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH)
+                    fine -= IGP01E1000_ANALOG_FUSE_FINE_10;
+
+                fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) |
+                        (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) |
+                        (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK);
+
+                e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused);
+                e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS,
+                                    IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL);
+            }
+        }
+    }
+}
 
 /******************************************************************************
  * Set the mac type member in the hw struct.
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
 int32_t
@@ -97,30 +224,99 @@ e1000_set_mac_type(struct e1000_hw *hw)
     case E1000_DEV_ID_82545EM_FIBER:
         hw->mac_type = e1000_82545;
         break;
+    case E1000_DEV_ID_82545GM_COPPER:
+    case E1000_DEV_ID_82545GM_FIBER:
+    case E1000_DEV_ID_82545GM_SERDES:
+        hw->mac_type = e1000_82545_rev_3;
+        break;
     case E1000_DEV_ID_82546EB_COPPER:
     case E1000_DEV_ID_82546EB_FIBER:
+    case E1000_DEV_ID_82546EB_QUAD_COPPER:
         hw->mac_type = e1000_82546;
         break;
+    case E1000_DEV_ID_82546GB_COPPER:
+    case E1000_DEV_ID_82546GB_FIBER:
+    case E1000_DEV_ID_82546GB_SERDES:
+        hw->mac_type = e1000_82546_rev_3;
+        break;
+    case E1000_DEV_ID_82541EI:
+    case E1000_DEV_ID_82541EI_MOBILE:
+        hw->mac_type = e1000_82541;
+        break;
+    case E1000_DEV_ID_82541ER:
+    case E1000_DEV_ID_82541GI:
+    case E1000_DEV_ID_82541GI_MOBILE:
+        hw->mac_type = e1000_82541_rev_2;
+        break;
+    case E1000_DEV_ID_82547EI:
+        hw->mac_type = e1000_82547;
+        break;
+    case E1000_DEV_ID_82547GI:
+        hw->mac_type = e1000_82547_rev_2;
+        break;
     default:
         /* Should never have loaded on this device */
         return -E1000_ERR_MAC_TYPE;
     }
+
     return E1000_SUCCESS;
 }
+
+/*****************************************************************************
+ * Set media type and TBI compatibility.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * **************************************************************************/
+void
+e1000_set_media_type(struct e1000_hw *hw)
+{
+    uint32_t status;
+
+    DEBUGFUNC("e1000_set_media_type");
+
+    if(hw->mac_type != e1000_82543) {
+        /* tbi_compatibility is only valid on 82543 */
+        hw->tbi_compatibility_en = FALSE;
+    }
+
+    switch (hw->device_id) {
+    case E1000_DEV_ID_82545GM_SERDES:
+    case E1000_DEV_ID_82546GB_SERDES:
+        hw->media_type = e1000_media_type_internal_serdes;
+        break;
+    default:
+        if(hw->mac_type >= e1000_82543) {
+            status = E1000_READ_REG(hw, STATUS);
+            if(status & E1000_STATUS_TBIMODE) {
+                hw->media_type = e1000_media_type_fiber;
+                /* tbi_compatibility not valid on fiber */
+                hw->tbi_compatibility_en = FALSE;
+            } else {
+                hw->media_type = e1000_media_type_copper;
+            }
+        } else {
+            /* This is an 82542 (fiber only) */
+            hw->media_type = e1000_media_type_fiber;
+        }
+    }
+}
+
 /******************************************************************************
  * Reset the transmit and receive units; mask and clear all interrupts.
  *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
-void
+int32_t
 e1000_reset_hw(struct e1000_hw *hw)
 {
     uint32_t ctrl;
     uint32_t ctrl_ext;
     uint32_t icr;
     uint32_t manc;
+    uint32_t led_ctrl;
 
     DEBUGFUNC("e1000_reset_hw");
+
     /* For 82542 (rev 2.0), disable MWI before issuing a device reset */
     if(hw->mac_type == e1000_82542_rev2_0) {
         DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
@@ -144,42 +340,93 @@ e1000_reset_hw(struct e1000_hw *hw)
 
     /* Delay to allow any outstanding PCI transactions to complete before
      * resetting the device
-     */ 
-    DEBUGOUT("Before delay\n");
+     */
     msec_delay(10);
 
+    ctrl = E1000_READ_REG(hw, CTRL);
+
+    /* Must reset the PHY before resetting the MAC */
+    if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+        E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST));
+        msec_delay(5);
+    }
+
     /* Issue a global reset to the MAC.  This will reset the chip's
      * transmit, receive, DMA, and link units.  It will not effect
      * the current PCI configuration.  The global reset bit is self-
      * clearing, and should clear within a microsecond.
      */
     DEBUGOUT("Issuing a global reset to MAC\n");
-    ctrl = E1000_READ_REG(hw, CTRL);
 
-    if(hw->mac_type > e1000_82543)
-        E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST));
-    else
-        E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+    switch(hw->mac_type) {
+        case e1000_82544:
+        case e1000_82540:
+        case e1000_82545:
+        case e1000_82546:
+        case e1000_82541:
+        case e1000_82541_rev_2:
+            /* These controllers can't ack the 64-bit write when issuing the
+             * reset, so use IO-mapping as a workaround to issue the reset */
+            E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST));
+            break;
+        case e1000_82545_rev_3:
+        case e1000_82546_rev_3:
+            /* Reset is performed on a shadow of the control register */
+            E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST));
+            break;
+        default:
+            E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+            break;
+    }
 
-    /* Force a reload from the EEPROM if necessary */
-    if(hw->mac_type < e1000_82540) {
-        /* Wait for reset to complete */
-        udelay(10);
-        ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
-        ctrl_ext |= E1000_CTRL_EXT_EE_RST;
-        E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
-        E1000_WRITE_FLUSH(hw);
-        /* Wait for EEPROM reload */
-        msec_delay(2);
-    } else {
-        /* Wait for EEPROM reload (it happens automatically) */
-        msec_delay(4);
-        /* Dissable HW ARPs on ASF enabled adapters */
+    /* After MAC reset, force reload of EEPROM to restore power-on settings to
+     * device.  Later controllers reload the EEPROM automatically, so just wait
+     * for reload to complete.
+     */
+    switch(hw->mac_type) {
+        case e1000_82542_rev2_0:
+        case e1000_82542_rev2_1:
+        case e1000_82543:
+        case e1000_82544:
+            /* Wait for reset to complete */
+            usec_delay(10);
+            ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+            ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+            E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+            E1000_WRITE_FLUSH(hw);
+            /* Wait for EEPROM reload */
+            msec_delay(2);
+            break;
+        case e1000_82541:
+        case e1000_82541_rev_2:
+        case e1000_82547:
+        case e1000_82547_rev_2:
+            /* Wait for EEPROM reload */
+            msec_delay(20);
+            break;
+        default:
+            /* Wait for EEPROM reload (it happens automatically) */
+            msec_delay(5);
+            break;
+    }
+
+    /* Disable HW ARPs on ASF enabled adapters */
+    if(hw->mac_type >= e1000_82540) {
         manc = E1000_READ_REG(hw, MANC);
         manc &= ~(E1000_MANC_ARP_EN);
         E1000_WRITE_REG(hw, MANC, manc);
     }
-    
+
+    if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+        e1000_phy_init_script(hw);
+
+        /* Configure activity LED after PHY reset */
+        led_ctrl = E1000_READ_REG(hw, LEDCTL);
+        led_ctrl &= IGP_ACTIVITY_LED_MASK;
+        led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+        E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+    }
+
     /* Clear interrupt mask to stop board from generating interrupts */
     DEBUGOUT("Masking off all interrupts\n");
     E1000_WRITE_REG(hw, IMC, 0xffffffff);
@@ -192,14 +439,16 @@ e1000_reset_hw(struct e1000_hw *hw)
         if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
             e1000_pci_set_mwi(hw);
     }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Performs basic configuration of the adapter.
  *
  * hw - Struct containing variables accessed by shared code
- * 
- * Assumes that the controller has previously been reset and is in a 
+ *
+ * Assumes that the controller has previously been reset and is in a
  * post-reset uninitialized state. Initializes the receive address registers,
  * multicast table, and VLAN filter table. Calls routines to setup link
  * configuration and flow control settings. Clears all on-chip counters. Leaves
@@ -208,7 +457,7 @@ e1000_reset_hw(struct e1000_hw *hw)
 int32_t
 e1000_init_hw(struct e1000_hw *hw)
 {
-    uint32_t ctrl, status;
+    uint32_t ctrl;
     uint32_t i;
     int32_t ret_val;
     uint16_t pcix_cmd_word;
@@ -219,31 +468,13 @@ e1000_init_hw(struct e1000_hw *hw)
     DEBUGFUNC("e1000_init_hw");
 
     /* Initialize Identification LED */
-    ret_val = e1000_id_led_init(hw);
-    if(ret_val < 0) {
+    if((ret_val = e1000_id_led_init(hw))) {
         DEBUGOUT("Error Initializing Identification LED\n");
         return ret_val;
     }
-    
-    /* Set the Media Type and exit with error if it is not valid. */
-    if(hw->mac_type != e1000_82543) {
-        /* tbi_compatibility is only valid on 82543 */
-        hw->tbi_compatibility_en = FALSE;
-    }
 
-    if(hw->mac_type >= e1000_82543) {
-        status = E1000_READ_REG(hw, STATUS);
-        if(status & E1000_STATUS_TBIMODE) {
-            hw->media_type = e1000_media_type_fiber;
-            /* tbi_compatibility not valid on fiber */
-            hw->tbi_compatibility_en = FALSE;
-        } else {
-            hw->media_type = e1000_media_type_copper;
-        }
-    } else {
-        /* This is an 82542 (fiber only) */
-        hw->media_type = e1000_media_type_fiber;
-    }
+    /* Set the media type and TBI compatibility */
+    e1000_set_media_type(hw);
 
     /* Disabling VLAN filtering. */
     DEBUGOUT("Initializing the IEEE VLAN\n");
@@ -288,21 +519,30 @@ e1000_init_hw(struct e1000_hw *hw)
         E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR);
     }
 
-    /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
-    if(hw->bus_type == e1000_bus_type_pcix) {
-        e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
-        e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word);
-        cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
-            PCIX_COMMAND_MMRBC_SHIFT;
-        stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
-            PCIX_STATUS_HI_MMRBC_SHIFT;
-        if(stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
-            stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
-        if(cmd_mmrbc > stat_mmrbc) {
-            pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
-            pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
-            e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+        break;
+    default:
+        /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
+        if(hw->bus_type == e1000_bus_type_pcix) {
+            e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+            e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
+                &pcix_stat_hi_word);
+            cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
+                PCIX_COMMAND_MMRBC_SHIFT;
+            stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
+                PCIX_STATUS_HI_MMRBC_SHIFT;
+            if(stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
+                stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
+            if(cmd_mmrbc > stat_mmrbc) {
+                pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
+                pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
+                e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
+                    &pcix_cmd_word);
+            }
         }
+        break;
     }
 
     /* Call a subroutine to configure the link and setup flow control. */
@@ -325,15 +565,55 @@ e1000_init_hw(struct e1000_hw *hw)
     return ret_val;
 }
 
+/******************************************************************************
+ * Adjust SERDES output amplitude based on EEPROM setting.
+ *
+ * hw - Struct containing variables accessed by shared code.
+ *****************************************************************************/
+static int32_t
+e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
+{
+    uint16_t eeprom_data;
+    int32_t  ret_val;
+
+    DEBUGFUNC("e1000_adjust_serdes_amplitude");
+
+    if(hw->media_type != e1000_media_type_internal_serdes)
+        return E1000_SUCCESS;
+
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+        break;
+    default:
+        return E1000_SUCCESS;
+    }
+
+    if ((ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1,
+                                     &eeprom_data))) {
+        return ret_val;
+    }
+
+    if(eeprom_data != EEPROM_RESERVED_WORD) {
+        /* Adjust SERDES output amplitude only. */
+        eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; 
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL,
+                                          eeprom_data)))
+            return ret_val;
+    }
+
+    return E1000_SUCCESS;
+}
+
 /******************************************************************************
  * Configures flow control and link settings.
- * 
+ *
  * hw - Struct containing variables accessed by shared code
- * 
+ *
  * Determines which flow control settings to use. Calls the apropriate media-
  * specific link configuration function. Configures the flow control settings.
  * Assuming the adapter has a valid link partner, a valid link should be
- * established. Assumes the hardware has previously been reset and the 
+ * established. Assumes the hardware has previously been reset and the
  * transmitter and receiver are not enabled.
  *****************************************************************************/
 int32_t
@@ -353,7 +633,7 @@ e1000_setup_link(struct e1000_hw *hw)
      * control setting, then the variable hw->fc will
      * be initialized based on a value in the EEPROM.
      */
-    if(e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, &eeprom_data) < 0) {
+    if(e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data) < 0) {
         DEBUGOUT("EEPROM Read Error\n");
         return -E1000_ERR_EEPROM;
     }
@@ -361,7 +641,7 @@ e1000_setup_link(struct e1000_hw *hw)
     if(hw->fc == e1000_fc_default) {
         if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0)
             hw->fc = e1000_fc_none;
-        else if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 
+        else if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) ==
                 EEPROM_WORD0F_ASM_DIR)
             hw->fc = e1000_fc_tx_pause;
         else
@@ -390,15 +670,15 @@ e1000_setup_link(struct e1000_hw *hw)
      * or e1000_phy_setup() is called.
      */
     if(hw->mac_type == e1000_82543) {
-        ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << 
+        ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) <<
                     SWDPIO__EXT_SHIFT);
         E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
     }
 
     /* Call the necessary subroutine to configure the link. */
-    ret_val = (hw->media_type == e1000_media_type_fiber) ?
-              e1000_setup_fiber_link(hw) :
-              e1000_setup_copper_link(hw);
+    ret_val = (hw->media_type == e1000_media_type_copper) ?
+              e1000_setup_copper_link(hw) :
+              e1000_setup_fiber_serdes_link(hw);
 
     /* Initialize the flow control address, type, and PAUSE timer
      * registers to their default values.  This is done even if flow
@@ -416,7 +696,7 @@ e1000_setup_link(struct e1000_hw *hw)
      * these registers will be set to a default threshold that may be
      * adjusted later by the driver's runtime code.  However, if the
      * ability to transmit pause frames in not enabled, then these
-     * registers will be set to 0. 
+     * registers will be set to 0.
      */
     if(!(hw->fc & e1000_fc_tx_pause)) {
         E1000_WRITE_REG(hw, FCRTL, 0);
@@ -437,7 +717,7 @@ e1000_setup_link(struct e1000_hw *hw)
 }
 
 /******************************************************************************
- * Sets up link for a fiber based adapter
+ * Sets up link for a fiber based or serdes based adapter
  *
  * hw - Struct containing variables accessed by shared code
  *
@@ -445,41 +725,50 @@ e1000_setup_link(struct e1000_hw *hw)
  * link. Assumes the hardware has been previously reset and the transmitter
  * and receiver are not enabled.
  *****************************************************************************/
-static int32_t 
-e1000_setup_fiber_link(struct e1000_hw *hw)
+static int32_t
+e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
 {
     uint32_t ctrl;
     uint32_t status;
     uint32_t txcw = 0;
     uint32_t i;
-    uint32_t signal;
+    uint32_t signal = 0;
     int32_t ret_val;
 
-    DEBUGFUNC("e1000_setup_fiber_link");
+    DEBUGFUNC("e1000_setup_fiber_serdes_link");
 
-    /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be 
-     * set when the optics detect a signal. On older adapters, it will be 
-     * cleared when there is a signal
+    /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+     * set when the optics detect a signal. On older adapters, it will be
+     * cleared when there is a signal.  This applies to fiber media only.
+     * If we're on serdes media, adjust the output amplitude to value set in
+     * the EEPROM.
      */
     ctrl = E1000_READ_REG(hw, CTRL);
-    if(hw->mac_type > e1000_82544) signal = E1000_CTRL_SWDPIN1;
-    else signal = 0;
-   
+    if(hw->media_type == e1000_media_type_fiber)
+        signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+
+    if((ret_val = e1000_adjust_serdes_amplitude(hw)))
+        return ret_val;
+
     /* Take the link out of reset */
     ctrl &= ~(E1000_CTRL_LRST);
-    
+
+    /* Adjust VCO speed to improve BER performance */
+    if((ret_val = e1000_set_vco_speed(hw)))
+        return ret_val;
+
     e1000_config_collision_dist(hw);
 
     /* Check for a software override of the flow control settings, and setup
      * the device accordingly.  If auto-negotiation is enabled, then software
      * will have to set the "PAUSE" bits to the correct value in the Tranmsit
      * Config Word Register (TXCW) and re-start auto-negotiation.  However, if
-     * auto-negotiation is disabled, then software will have to manually 
+     * auto-negotiation is disabled, then software will have to manually
      * configure the two flow control enable bits in the CTRL register.
      *
      * The possible values of the "fc" parameter are:
      *      0:  Flow control is completely disabled
-     *      1:  Rx flow control is enabled (we can receive pause frames, but 
+     *      1:  Rx flow control is enabled (we can receive pause frames, but
      *          not send pause frames).
      *      2:  Tx flow control is enabled (we can send pause frames but we do
      *          not support receiving pause frames).
@@ -491,8 +780,8 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
         txcw = (E1000_TXCW_ANE | E1000_TXCW_FD);
         break;
     case e1000_fc_rx_pause:
-        /* RX Flow control is enabled and TX Flow control is disabled by a 
-         * software over-ride. Since there really isn't a way to advertise 
+        /* RX Flow control is enabled and TX Flow control is disabled by a
+         * software over-ride. Since there really isn't a way to advertise
          * that we are capable of RX Pause ONLY, we will advertise that we
          * support both symmetric and asymmetric RX PAUSE. Later, we will
          *  disable the adapter's ability to send PAUSE frames.
@@ -500,7 +789,7 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
         txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
         break;
     case e1000_fc_tx_pause:
-        /* TX Flow control is enabled, and RX Flow control is disabled, by a 
+        /* TX Flow control is enabled, and RX Flow control is disabled, by a
          * software over-ride.
          */
         txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR);
@@ -531,11 +820,13 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
     msec_delay(1);
 
     /* If we have a signal (the cable is plugged in) then poll for a "Link-Up"
-     * indication in the Device Status Register.  Time-out if a link isn't 
-     * seen in 500 milliseconds seconds (Auto-negotiation should complete in 
+     * indication in the Device Status Register.  Time-out if a link isn't
+     * seen in 500 milliseconds seconds (Auto-negotiation should complete in
      * less than 500 milliseconds even if the other end is doing it in SW).
+     * For internal serdes, we just assume a signal is present, then poll.
      */
-    if((E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
+    if(hw->media_type == e1000_media_type_internal_serdes ||
+       (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
         DEBUGOUT("Looking for Link\n");
         for(i = 0; i < (LINK_UP_TIMEOUT / 10); i++) {
             msec_delay(10);
@@ -543,19 +834,20 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
             if(status & E1000_STATUS_LU) break;
         }
         if(i == (LINK_UP_TIMEOUT / 10)) {
-            /* AutoNeg failed to achieve a link, so we'll call 
-             * e1000_check_for_link. This routine will force the link up if we
-             * detect a signal. This will allow us to communicate with
-             * non-autonegotiating link partners.
-             */
             DEBUGOUT("Never got a valid link from auto-neg!!!\n");
             hw->autoneg_failed = 1;
-            ret_val = e1000_check_for_link(hw);
-            if(ret_val < 0) {
-                DEBUGOUT("Error while checking for link\n");
-                return ret_val;
+            if(hw->media_type == e1000_media_type_fiber) {
+                /* AutoNeg failed to achieve a link, so we'll call
+                 * e1000_check_for_link. This routine will force the link up if
+                 * we detect a signal. This will allow us to communicate with
+                 * non-autonegotiating link partners.
+                 */
+                if((ret_val = e1000_check_for_link(hw))) {
+                    DEBUGOUT("Error while checking for link\n");
+                    return ret_val;
+                }
+                hw->autoneg_failed = 0;
             }
-            hw->autoneg_failed = 0;
         } else {
             hw->autoneg_failed = 0;
             DEBUGOUT("Valid Link Found\n");
@@ -563,7 +855,7 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
     } else {
         DEBUGOUT("No Signal Detected\n");
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -571,10 +863,11 @@ e1000_setup_fiber_link(struct e1000_hw *hw)
 *
 * hw - Struct containing variables accessed by shared code
 ******************************************************************************/
-static int32_t 
+static int32_t
 e1000_setup_copper_link(struct e1000_hw *hw)
 {
     uint32_t ctrl;
+    uint32_t led_ctrl;
     int32_t ret_val;
     uint16_t i;
     uint16_t phy_data;
@@ -597,166 +890,281 @@ e1000_setup_copper_link(struct e1000_hw *hw)
     }
 
     /* Make sure we have a valid PHY */
-    ret_val = e1000_detect_gig_phy(hw);
-    if(ret_val < 0) {
+    if((ret_val = e1000_detect_gig_phy(hw))) {
         DEBUGOUT("Error, did not detect valid phy.\n");
         return ret_val;
     }
     DEBUGOUT1("Phy ID = %x \n", hw->phy_id);
 
-    /* Enable CRS on TX. This must be set for half-duplex operation. */
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+    if(hw->mac_type <= e1000_82543 ||
+       hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
+       hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2)
+        hw->phy_reset_disable = FALSE;
 
-    /* Options:
-     *   MDI/MDI-X = 0 (default)
-     *   0 - Auto for all speeds
-     *   1 - MDI mode
-     *   2 - MDI-X mode
-     *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
-     */
-    phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+    if(!hw->phy_reset_disable) {
+        if (hw->phy_type == e1000_phy_igp) {
 
-    switch (hw->mdix) {
-    case 1:
-        phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
-        break;
-    case 2:
-        phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
-        break;
-    case 3:
-        phy_data |= M88E1000_PSCR_AUTO_X_1000T;
-        break;
-    case 0:
-    default:
-        phy_data |= M88E1000_PSCR_AUTO_X_MODE;
-        break;
-    }
+            if((ret_val = e1000_phy_reset(hw))) {
+                DEBUGOUT("Error Resetting the PHY\n");
+                return ret_val;
+            }
 
-    /* Options:
-     *   disable_polarity_correction = 0 (default)
-     *       Automatic Correction for Reversed Cable Polarity
-     *   0 - Disabled
-     *   1 - Enabled
-     */
-    phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
-    if(hw->disable_polarity_correction == 1)
-        phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
-    if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
+            /* Wait 10ms for MAC to configure PHY from eeprom settings */
+            msec_delay(15);
 
-    /* Force TX_CLK in the Extended PHY Specific Control Register
-     * to 25MHz clock.
-     */
-    if(e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= M88E1000_EPSCR_TX_CLK_25;
-
-    if (hw->phy_revision < M88E1011_I_REV_4) {
-        /* Configure Master and Slave downshift values */
-        phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
-                      M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
-        phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
-                     M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
-        if(e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
-    }
+            /* Configure activity LED after PHY reset */
+            led_ctrl = E1000_READ_REG(hw, LEDCTL);
+            led_ctrl &= IGP_ACTIVITY_LED_MASK;
+            led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+            E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
 
-    /* SW Reset the PHY so all changes take effect */
-    ret_val = e1000_phy_reset(hw);
-    if(ret_val < 0) {
-        DEBUGOUT("Error Resetting the PHY\n");
-        return ret_val;
-    }
-    
-    /* Options:
-     *   autoneg = 1 (default)
-     *      PHY will advertise value(s) parsed from
-     *      autoneg_advertised and fc
-     *   autoneg = 0
-     *      PHY will be set to 10H, 10F, 100H, or 100F
-     *      depending on value parsed from forced_speed_duplex.
-     */
+            /* disable lplu d3 during driver init */
+            if((ret_val = e1000_set_d3_lplu_state(hw, FALSE))) {
+                DEBUGOUT("Error Disabling LPLU D3\n");
+                return ret_val;
+            }
 
-    /* Is autoneg enabled?  This is enabled by default or by software override.
-     * If so, call e1000_phy_setup_autoneg routine to parse the
-     * autoneg_advertised and fc options. If autoneg is NOT enabled, then the
-     * user should have provided a speed/duplex override.  If so, then call
-     * e1000_phy_force_speed_duplex to parse and set this up.
-     */
-    if(hw->autoneg) {
-        /* Perform some bounds checking on the hw->autoneg_advertised
-         * parameter.  If this variable is zero, then set it to the default.
-         */
-        hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
+            /* Configure mdi-mdix settings */
+            if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                             &phy_data)))
+                return ret_val;
 
-        /* If autoneg_advertised is zero, we assume it was not defaulted
-         * by the calling code so we set to advertise full capability.
-         */
-        if(hw->autoneg_advertised == 0)
-            hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+            if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+                hw->dsp_config_state = e1000_dsp_config_disabled;
+                /* Force MDI for IGP B-0 PHY */
+                phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX |
+                              IGP01E1000_PSCR_FORCE_MDI_MDIX);
+                hw->mdix = 1;
 
-        DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
-        ret_val = e1000_phy_setup_autoneg(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Setting up Auto-Negotiation\n");
-            return ret_val;
+            } else {
+                hw->dsp_config_state = e1000_dsp_config_enabled;
+                phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+
+                switch (hw->mdix) {
+                case 1:
+                    phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+                    break;
+                case 2:
+                    phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
+                    break;
+                case 0:
+                default:
+                    phy_data |= IGP01E1000_PSCR_AUTO_MDIX;
+                    break;
+                }
+            }
+            if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                              phy_data)))
+                return ret_val;
+
+            /* set auto-master slave resolution settings */
+            if(hw->autoneg) {
+                e1000_ms_type phy_ms_setting = hw->master_slave;
+
+                if(hw->ffe_config_state == e1000_ffe_config_active)
+                    hw->ffe_config_state = e1000_ffe_config_enabled;
+
+                if(hw->dsp_config_state == e1000_dsp_config_activated)
+                    hw->dsp_config_state = e1000_dsp_config_enabled;
+
+                /* when autonegotiation advertisment is only 1000Mbps then we
+                 * should disable SmartSpeed and enable Auto MasterSlave
+                 * resolution as hardware default. */
+                if(hw->autoneg_advertised == ADVERTISE_1000_FULL) {
+                    /* Disable SmartSpeed */
+                    if((ret_val = e1000_read_phy_reg(hw,
+                                                    IGP01E1000_PHY_PORT_CONFIG,
+                                                    &phy_data)))
+                        return ret_val;
+                    phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+                    if((ret_val = e1000_write_phy_reg(hw,
+                                                     IGP01E1000_PHY_PORT_CONFIG,
+                                                     phy_data)))
+                        return ret_val;
+                    /* Set auto Master/Slave resolution process */
+                    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+                                                     &phy_data)))
+                        return ret_val;
+                    phy_data &= ~CR_1000T_MS_ENABLE;
+                    if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+                                                      phy_data)))
+                        return ret_val;
+                }
+
+                if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+                                                 &phy_data)))
+                    return ret_val;
+
+                /* load defaults for future use */
+                hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ?
+                                            ((phy_data & CR_1000T_MS_VALUE) ?
+                                             e1000_ms_force_master :
+                                             e1000_ms_force_slave) :
+                                             e1000_ms_auto;
+
+                switch (phy_ms_setting) {
+                case e1000_ms_force_master:
+                    phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
+                    break;
+                case e1000_ms_force_slave:
+                    phy_data |= CR_1000T_MS_ENABLE;
+                    phy_data &= ~(CR_1000T_MS_VALUE);
+                    break;
+                case e1000_ms_auto:
+                    phy_data &= ~CR_1000T_MS_ENABLE;
+                default:
+                    break;
+                }
+                if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+                                                  phy_data)))
+                    return ret_val;
+            }
+        } else {
+            /* Enable CRS on TX. This must be set for half-duplex operation. */
+            if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                             &phy_data)))
+                return ret_val;
+
+            phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+
+            /* Options:
+             *   MDI/MDI-X = 0 (default)
+             *   0 - Auto for all speeds
+             *   1 - MDI mode
+             *   2 - MDI-X mode
+             *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+             */
+            phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+
+            switch (hw->mdix) {
+            case 1:
+                phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
+                break;
+            case 2:
+                phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
+                break;
+            case 3:
+                phy_data |= M88E1000_PSCR_AUTO_X_1000T;
+                break;
+            case 0:
+            default:
+                phy_data |= M88E1000_PSCR_AUTO_X_MODE;
+                break;
+            }
+
+            /* Options:
+             *   disable_polarity_correction = 0 (default)
+             *       Automatic Correction for Reversed Cable Polarity
+             *   0 - Disabled
+             *   1 - Enabled
+             */
+            phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
+            if(hw->disable_polarity_correction == 1)
+                phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
+            if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                              phy_data)))
+                return ret_val;
+
+            /* Force TX_CLK in the Extended PHY Specific Control Register
+             * to 25MHz clock.
+             */
+            if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                             &phy_data)))
+                return ret_val;
+
+            phy_data |= M88E1000_EPSCR_TX_CLK_25;
+
+            if (hw->phy_revision < M88E1011_I_REV_4) {
+                /* Configure Master and Slave downshift values */
+                phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
+                              M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
+                phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
+                             M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
+                if((ret_val = e1000_write_phy_reg(hw,
+                                                  M88E1000_EXT_PHY_SPEC_CTRL,
+                                                  phy_data)))
+                    return ret_val;
+            }
+
+            /* SW Reset the PHY so all changes take effect */
+            if((ret_val = e1000_phy_reset(hw))) {
+                DEBUGOUT("Error Resetting the PHY\n");
+                return ret_val;
+            }
         }
-        DEBUGOUT("Restarting Auto-Neg\n");
 
-        /* Restart auto-negotiation by setting the Auto Neg Enable bit and
-         * the Auto Neg Restart bit in the PHY control register.
+        /* Options:
+         *   autoneg = 1 (default)
+         *      PHY will advertise value(s) parsed from
+         *      autoneg_advertised and fc
+         *   autoneg = 0
+         *      PHY will be set to 10H, 10F, 100H, or 100F
+         *      depending on value parsed from forced_speed_duplex.
          */
-        if(e1000_read_phy_reg(hw, PHY_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
-        if(e1000_write_phy_reg(hw, PHY_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
 
-        /* Does the user want to wait for Auto-Neg to complete here, or
-         * check at a later time (for example, callback routine).
+        /* Is autoneg enabled?  This is enabled by default or by software
+         * override.  If so, call e1000_phy_setup_autoneg routine to parse the
+         * autoneg_advertised and fc options. If autoneg is NOT enabled, then
+         * the user should have provided a speed/duplex override.  If so, then
+         * call e1000_phy_force_speed_duplex to parse and set this up.
          */
-        if(hw->wait_autoneg_complete) {
-            ret_val = e1000_wait_autoneg(hw);
-            if(ret_val < 0) {
-                DEBUGOUT("Error while waiting for autoneg to complete\n");
+        if(hw->autoneg) {
+            /* Perform some bounds checking on the hw->autoneg_advertised
+             * parameter.  If this variable is zero, then set it to the default.
+             */
+            hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+            /* If autoneg_advertised is zero, we assume it was not defaulted
+             * by the calling code so we set to advertise full capability.
+             */
+            if(hw->autoneg_advertised == 0)
+                hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+            DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
+            if((ret_val = e1000_phy_setup_autoneg(hw))) {
+                DEBUGOUT("Error Setting up Auto-Negotiation\n");
+                return ret_val;
+            }
+            DEBUGOUT("Restarting Auto-Neg\n");
+
+            /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+             * the Auto Neg Restart bit in the PHY control register.
+             */
+            if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+                return ret_val;
+
+            phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+            if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+                return ret_val;
+
+            /* Does the user want to wait for Auto-Neg to complete here, or
+             * check at a later time (for example, callback routine).
+             */
+            if(hw->wait_autoneg_complete) {
+                if((ret_val = e1000_wait_autoneg(hw))) {
+                    DEBUGOUT("Error while waiting for autoneg to complete\n");
+                    return ret_val;
+                }
+            }
+            hw->get_link_status = TRUE;
+        } else {
+            DEBUGOUT("Forcing speed and duplex\n");
+            if((ret_val = e1000_phy_force_speed_duplex(hw))) {
+                DEBUGOUT("Error Forcing Speed and Duplex\n");
                 return ret_val;
             }
         }
-    } else {
-        DEBUGOUT("Forcing speed and duplex\n");
-        ret_val = e1000_phy_force_speed_duplex(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Forcing Speed and Duplex\n");
-            return ret_val;
-        }
-    }
+    } /* !hw->phy_reset_disable */
 
     /* Check link status. Wait up to 100 microseconds for link to become
      * valid.
      */
     for(i = 0; i < 10; i++) {
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+
         if(phy_data & MII_SR_LINK_STATUS) {
             /* We have link, so we need to finish the config process:
              *   1) Set up the MAC to the current PHY speed/duplex
@@ -769,25 +1177,31 @@ e1000_setup_copper_link(struct e1000_hw *hw)
             if(hw->mac_type >= e1000_82544) {
                 e1000_config_collision_dist(hw);
             } else {
-                ret_val = e1000_config_mac_to_phy(hw);
-                if(ret_val < 0) {
+                if((ret_val = e1000_config_mac_to_phy(hw))) {
                     DEBUGOUT("Error configuring MAC to PHY settings\n");
                     return ret_val;
-                  }
+                }
             }
-            ret_val = e1000_config_fc_after_link_up(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_config_fc_after_link_up(hw))) {
                 DEBUGOUT("Error Configuring Flow Control\n");
                 return ret_val;
             }
             DEBUGOUT("Valid link established!!!\n");
-            return 0;
+
+            if(hw->phy_type == e1000_phy_igp) {
+                if((ret_val = e1000_config_dsp_after_link_change(hw, TRUE))) {
+                    DEBUGOUT("Error Configuring DSP after link up\n");
+                    return ret_val;
+                }
+            }
+            DEBUGOUT("Valid link established!!!\n");
+            return E1000_SUCCESS;
         }
-        udelay(10);
+        usec_delay(10);
     }
 
     DEBUGOUT("Unable to establish link!!!\n");
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -798,22 +1212,20 @@ e1000_setup_copper_link(struct e1000_hw *hw)
 int32_t
 e1000_phy_setup_autoneg(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t mii_autoneg_adv_reg;
     uint16_t mii_1000t_ctrl_reg;
 
     DEBUGFUNC("e1000_phy_setup_autoneg");
 
     /* Read the MII Auto-Neg Advertisement Register (Address 4). */
-    if(e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+                                     &mii_autoneg_adv_reg)))
+        return ret_val;
 
     /* Read the MII 1000Base-T Control Register (Address 9). */
-    if(e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg)))
+        return ret_val;
 
     /* Need to parse both autoneg_advertised and fc and set up
      * the appropriate PHY registers.  First we will parse for
@@ -919,18 +1331,16 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw)
         return -E1000_ERR_CONFIG;
     }
 
-    if(e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV,
+                                      mii_autoneg_adv_reg)))
+        return ret_val;
 
     DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
 
-    if(e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
-    return 0;
+    if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg)))
+        return ret_val;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -966,10 +1376,8 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw)
     ctrl &= ~E1000_CTRL_ASDE;
 
     /* Read the MII Control Register. */
-    if(e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg)))
+        return ret_val;
 
     /* We need to disable autoneg in order to force link and duplex. */
 
@@ -1014,30 +1422,44 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw)
     /* Write the configured values back to the Device Control Reg. */
     E1000_WRITE_REG(hw, CTRL, ctrl);
 
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if (hw->phy_type == e1000_phy_m88) {
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
 
-    /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI
-     * forced whenever speed are duplex are forced.
-     */
-    phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
-    if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
-    DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data);
+        /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI
+         * forced whenever speed are duplex are forced.
+         */
+        phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
 
-    /* Need to reset the PHY or these changes will be ignored */
-    mii_ctrl_reg |= MII_CR_RESET;
+        DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data);
 
-    /* Write back the modified PHY MII control register. */
-    if(e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
+        /* Need to reset the PHY or these changes will be ignored */
+        mii_ctrl_reg |= MII_CR_RESET;
+    } else {
+        /* Clear Auto-Crossover to force MDI manually.  IGP requires MDI
+         * forced whenever speed or duplex are forced.
+         */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+        phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                          phy_data)))
+            return ret_val;
     }
-    udelay(1);
+
+    /* Write back the modified PHY MII control register. */
+    if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg)))
+        return ret_val;
+
+    usec_delay(1);
 
     /* The wait_autoneg_complete flag may be a little misleading here.
      * Since we are forcing speed and duplex, Auto-Neg is not enabled.
@@ -1056,22 +1478,18 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw)
             /* Read the MII Status Register and wait for Auto-Neg Complete bit
              * to be set.
              */
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
             if(mii_status_reg & MII_SR_LINK_STATUS) break;
             msec_delay(100);
         }
         if(i == 0) { /* We didn't get link */
             /* Reset the DSP and wait again for link. */
-            
-            ret_val = e1000_phy_reset_dsp(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_phy_reset_dsp(hw))) {
                 DEBUGOUT("Error Resetting PHY DSP\n");
                 return ret_val;
             }
@@ -1083,44 +1501,42 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw)
             /* Read the MII Status Register and wait for Auto-Neg Complete bit
              * to be set.
              */
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
         }
     }
-    
-    /* Because we reset the PHY above, we need to re-force TX_CLK in the
-     * Extended PHY Specific Control Register to 25MHz clock.  This value
-     * defaults back to a 2.5MHz clock when the PHY is reset.
-     */
-    if(e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= M88E1000_EPSCR_TX_CLK_25;
-    if(e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
 
-    /* In addition, because of the s/w reset above, we need to enable CRS on
-     * TX.  This must be set for both full and half duplex operation.
-     */
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
-    if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
+    if (hw->phy_type == e1000_phy_m88) {
+        /* Because we reset the PHY above, we need to re-force TX_CLK in the
+         * Extended PHY Specific Control Register to 25MHz clock.  This value
+         * defaults back to a 2.5MHz clock when the PHY is reset.
+         */
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data |= M88E1000_EPSCR_TX_CLK_25;
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
+
+        /* In addition, because of the s/w reset above, we need to enable CRS on
+         * TX.  This must be set for both full and half duplex operation.
+         */
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
+
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1136,6 +1552,8 @@ e1000_config_collision_dist(struct e1000_hw *hw)
 {
     uint32_t tctl;
 
+    DEBUGFUNC("e1000_config_collision_dist");
+
     tctl = E1000_READ_REG(hw, TCTL);
 
     tctl &= ~E1000_TCTL_COLD;
@@ -1158,6 +1576,7 @@ static int32_t
 e1000_config_mac_to_phy(struct e1000_hw *hw)
 {
     uint32_t ctrl;
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_config_mac_to_phy");
@@ -1172,30 +1591,51 @@ e1000_config_mac_to_phy(struct e1000_hw *hw)
     /* Set up duplex in the Device Control and Transmit Control
      * registers depending on negotiated values.
      */
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    if(phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD;
-    else ctrl &= ~E1000_CTRL_FD;
+    if (hw->phy_type == e1000_phy_igp) {
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                         &phy_data)))
+            return ret_val;
 
-    e1000_config_collision_dist(hw);
+        if(phy_data & IGP01E1000_PSSR_FULL_DUPLEX) ctrl |= E1000_CTRL_FD;
+        else ctrl &= ~E1000_CTRL_FD;
 
-    /* Set up speed in the Device Control register depending on
-     * negotiated values.
-     */
-    if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS)
-        ctrl |= E1000_CTRL_SPD_1000;
-    else if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS)
-        ctrl |= E1000_CTRL_SPD_100;
+        e1000_config_collision_dist(hw);
+
+        /* Set up speed in the Device Control register depending on
+         * negotiated values.
+         */
+        if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+           IGP01E1000_PSSR_SPEED_1000MBPS)
+            ctrl |= E1000_CTRL_SPD_1000;
+        else if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+                IGP01E1000_PSSR_SPEED_100MBPS)
+            ctrl |= E1000_CTRL_SPD_100;
+    } else {
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
+        if(phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD;
+        else ctrl &= ~E1000_CTRL_FD;
+
+        e1000_config_collision_dist(hw);
+
+        /* Set up speed in the Device Control register depending on
+         * negotiated values.
+         */
+        if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS)
+            ctrl |= E1000_CTRL_SPD_1000;
+        else if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS)
+            ctrl |= E1000_CTRL_SPD_100;
+    }
     /* Write the configured values back to the Device Control Reg. */
     E1000_WRITE_REG(hw, CTRL, ctrl);
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Forces the MAC's flow control settings.
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  *
  * Sets the TFCE and RFCE bits in the device control register to reflect
@@ -1204,7 +1644,7 @@ e1000_config_mac_to_phy(struct e1000_hw *hw)
  * by the PHY rather than the MAC. Software must also configure these
  * bits when link is forced on a fiber connection.
  *****************************************************************************/
-static int32_t
+int32_t
 e1000_force_mac_fc(struct e1000_hw *hw)
 {
     uint32_t ctrl;
@@ -1257,12 +1697,12 @@ e1000_force_mac_fc(struct e1000_hw *hw)
         ctrl &= (~E1000_CTRL_TFCE);
 
     E1000_WRITE_REG(hw, CTRL, ctrl);
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Configures flow control settings after link is established
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  *
  * Should be called immediately after a valid link has been established.
@@ -1288,9 +1728,9 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
      * configuration of the MAC to match the "fc" parameter.
      */
     if(((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) ||
+       ((hw->media_type == e1000_media_type_internal_serdes) && (hw->autoneg_failed)) ||
        ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) {
-        ret_val = e1000_force_mac_fc(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_force_mac_fc(hw))) {
             DEBUGOUT("Error forcing flow control settings\n");
             return ret_val;
         }
@@ -1306,14 +1746,10 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
          * has completed.  We read this twice because this reg has
          * some "sticky" (latched) bits.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-            DEBUGOUT("PHY Read Error \n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-            DEBUGOUT("PHY Read Error \n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+            return ret_val;
 
         if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) {
             /* The AutoNeg process has completed, so we now need to
@@ -1322,14 +1758,12 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
              * Register (Address 5) to determine how flow control was
              * negotiated.
              */
-            if(e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_LP_ABILITY, &mii_nway_lp_ability_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+                                             &mii_nway_adv_reg)))
+                return ret_val;
+            if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+                                             &mii_nway_lp_ability_reg)))
+                return ret_val;
 
             /* Two bits in the Auto Negotiation Advertisement Register
              * (Address 4) and two bits in the Auto Negotiation Base
@@ -1435,7 +1869,7 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
                     hw->original_fc == e1000_fc_tx_pause) {
                 hw->fc = e1000_fc_none;
                 DEBUGOUT("Flow Control = NONE.\r\n");
-            } else {
+            } else if(!hw->fc_strict_ieee) {
                 hw->fc = e1000_fc_rx_pause;
                 DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
             }
@@ -1444,7 +1878,10 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
              * negotiated to HALF DUPLEX, flow control should not be
              * enabled per IEEE 802.3 spec.
              */
-            e1000_get_speed_and_duplex(hw, &speed, &duplex);
+            if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) {
+                DEBUGOUT("Error getting link speed and duplex\n");
+                return ret_val;
+            }
 
             if(duplex == HALF_DUPLEX)
                 hw->fc = e1000_fc_none;
@@ -1452,16 +1889,15 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
             /* Now we call a subroutine to actually force the MAC
              * controller to use the correct flow control settings.
              */
-            ret_val = e1000_force_mac_fc(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_force_mac_fc(hw))) {
                 DEBUGOUT("Error forcing flow control settings\n");
                 return ret_val;
-             }
+            }
         } else {
             DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\n");
         }
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1478,19 +1914,19 @@ e1000_check_for_link(struct e1000_hw *hw)
     uint32_t ctrl;
     uint32_t status;
     uint32_t rctl;
-    uint32_t signal;
+    uint32_t signal = 0;
     int32_t ret_val;
     uint16_t phy_data;
     uint16_t lp_capability;
 
     DEBUGFUNC("e1000_check_for_link");
-    
-    /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be 
-     * set when the optics detect a signal. On older adapters, it will be 
-     * cleared when there is a signal
+
+    /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+     * set when the optics detect a signal. On older adapters, it will be
+     * cleared when there is a signal.  This applies to fiber media only.
      */
-    if(hw->mac_type > e1000_82544) signal = E1000_CTRL_SWDPIN1;
-    else signal = 0;
+    if(hw->media_type == e1000_media_type_fiber)
+        signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
 
     ctrl = E1000_READ_REG(hw, CTRL);
     status = E1000_READ_REG(hw, STATUS);
@@ -1508,19 +1944,20 @@ e1000_check_for_link(struct e1000_hw *hw)
          * of the PHY.
          * Read the register twice since the link bit is sticky.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
 
         if(phy_data & MII_SR_LINK_STATUS) {
             hw->get_link_status = FALSE;
+            /* Check if there was DownShift, must be checked immediately after
+             * link-up */
+            e1000_check_downshift(hw);
+
         } else {
             /* No link detected */
+            e1000_config_dsp_after_link_change(hw, FALSE);
             return 0;
         }
 
@@ -1529,6 +1966,9 @@ e1000_check_for_link(struct e1000_hw *hw)
          */
         if(!hw->autoneg) return -E1000_ERR_CONFIG;
 
+        /* optimize the dsp settings for the igp phy */
+        e1000_config_dsp_after_link_change(hw, TRUE);
+
         /* We have a M88E1000 PHY and Auto-Neg is enabled.  If we
          * have Si on board that is 82544 or newer, Auto
          * Speed Detection takes care of MAC speed/duplex
@@ -1540,19 +1980,17 @@ e1000_check_for_link(struct e1000_hw *hw)
         if(hw->mac_type >= e1000_82544)
             e1000_config_collision_dist(hw);
         else {
-            ret_val = e1000_config_mac_to_phy(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_config_mac_to_phy(hw))) {
                 DEBUGOUT("Error configuring MAC to PHY settings\n");
                 return ret_val;
             }
         }
 
-        /* Configure Flow Control now that Auto-Neg has completed. First, we 
+        /* Configure Flow Control now that Auto-Neg has completed. First, we
          * need to restore the desired flow control settings because we may
          * have had to re-autoneg with a different link partner.
          */
-        ret_val = e1000_config_fc_after_link_up(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_config_fc_after_link_up(hw))) {
             DEBUGOUT("Error configuring flow control\n");
             return ret_val;
         }
@@ -1567,16 +2005,15 @@ e1000_check_for_link(struct e1000_hw *hw)
          * partner is TBI-based, and we turn on TBI Compatibility.
          */
         if(hw->tbi_compatibility_en) {
-            if(e1000_read_phy_reg(hw, PHY_LP_ABILITY, &lp_capability) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+                                             &lp_capability)))
+                return ret_val;
             if(lp_capability & (NWAY_LPAR_10T_HD_CAPS |
                                 NWAY_LPAR_10T_FD_CAPS |
                                 NWAY_LPAR_100TX_HD_CAPS |
                                 NWAY_LPAR_100TX_FD_CAPS |
                                 NWAY_LPAR_100T4_CAPS)) {
-                /* If our link partner advertises anything in addition to 
+                /* If our link partner advertises anything in addition to
                  * gigabit, we do not need to enable TBI compatibility.
                  */
                 if(hw->tbi_compatibility_on) {
@@ -1627,8 +2064,7 @@ e1000_check_for_link(struct e1000_hw *hw)
         E1000_WRITE_REG(hw, CTRL, ctrl);
 
         /* Configure Flow Control after forcing link up. */
-        ret_val = e1000_config_fc_after_link_up(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_config_fc_after_link_up(hw))) {
             DEBUGOUT("Error configuring flow control\n");
             return ret_val;
         }
@@ -1645,7 +2081,7 @@ e1000_check_for_link(struct e1000_hw *hw)
         E1000_WRITE_REG(hw, TXCW, hw->txcw);
         E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU));
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1655,12 +2091,14 @@ e1000_check_for_link(struct e1000_hw *hw)
  * speed - Speed of the connection
  * duplex - Duplex setting of the connection
  *****************************************************************************/
-void
+int32_t
 e1000_get_speed_and_duplex(struct e1000_hw *hw,
                            uint16_t *speed,
                            uint16_t *duplex)
 {
     uint32_t status;
+    int32_t ret_val;
+    uint16_t phy_data;
 
     DEBUGFUNC("e1000_get_speed_and_duplex");
 
@@ -1689,6 +2127,27 @@ e1000_get_speed_and_duplex(struct e1000_hw *hw,
         *speed = SPEED_1000;
         *duplex = FULL_DUPLEX;
     }
+
+    /* IGP01 PHY may advertise full duplex operation after speed downgrade even
+     * if it is operating at half duplex.  Here we set the duplex settings to
+     * match the duplex in the link partner's capabilities.
+     */
+    if(hw->phy_type == e1000_phy_igp && hw->speed_downgraded) {
+        if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data)))
+            return ret_val;
+
+        if(!(phy_data & NWAY_ER_LP_NWAY_CAPS))
+            *duplex = HALF_DUPLEX;
+        else {
+            if((ret_val == e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data)))
+                return ret_val;
+            if((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) ||
+               (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
+                *duplex = HALF_DUPLEX;
+        }
+    }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1699,6 +2158,7 @@ e1000_get_speed_and_duplex(struct e1000_hw *hw,
 int32_t
 e1000_wait_autoneg(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t i;
     uint16_t phy_data;
 
@@ -1710,20 +2170,16 @@ e1000_wait_autoneg(struct e1000_hw *hw)
         /* Read the MII Status Register and wait for Auto-Neg
          * Complete bit to be set.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
         if(phy_data & MII_SR_AUTONEG_COMPLETE) {
-            return 0;
+            return E1000_SUCCESS;
         }
         msec_delay(100);
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1737,11 +2193,11 @@ e1000_raise_mdi_clk(struct e1000_hw *hw,
                     uint32_t *ctrl)
 {
     /* Raise the clock input to the Management Data Clock (by setting the MDC
-     * bit), and then delay 2 microseconds.
+     * bit), and then delay 10 microseconds.
      */
     E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC));
     E1000_WRITE_FLUSH(hw);
-    udelay(2);
+    usec_delay(10);
 }
 
 /******************************************************************************
@@ -1755,11 +2211,11 @@ e1000_lower_mdi_clk(struct e1000_hw *hw,
                     uint32_t *ctrl)
 {
     /* Lower the clock input to the Management Data Clock (by clearing the MDC
-     * bit), and then delay 2 microseconds.
+     * bit), and then delay 10 microseconds.
      */
     E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC));
     E1000_WRITE_FLUSH(hw);
-    udelay(2);
+    usec_delay(10);
 }
 
 /******************************************************************************
@@ -1780,7 +2236,7 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw,
     uint32_t mask;
 
     /* We need to shift "count" number of bits out to the PHY. So, the value
-     * in the "data" parameter will be shifted out to the PHY one bit at a 
+     * in the "data" parameter will be shifted out to the PHY one bit at a
      * time. In order to do this, "data" must be broken down into bits.
      */
     mask = 0x01;
@@ -1803,7 +2259,7 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw,
         E1000_WRITE_REG(hw, CTRL, ctrl);
         E1000_WRITE_FLUSH(hw);
 
-        udelay(2);
+        usec_delay(10);
 
         e1000_raise_mdi_clk(hw, &ctrl);
         e1000_lower_mdi_clk(hw, &ctrl);
@@ -1817,7 +2273,7 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw,
 *
 * hw - Struct containing variables accessed by shared code
 *
-* Bits are shifted in in MSB to LSB order. 
+* Bits are shifted in in MSB to LSB order.
 ******************************************************************************/
 static uint16_t
 e1000_shift_in_mdi_bits(struct e1000_hw *hw)
@@ -1832,7 +2288,7 @@ e1000_shift_in_mdi_bits(struct e1000_hw *hw)
      * These two bits are ignored by us and thrown away. Bits are "shifted in"
      * by raising the input to the Management Data Clock (setting the MDC bit),
      * and then reading the value of the MDIO bit.
-     */ 
+     */
     ctrl = E1000_READ_REG(hw, CTRL);
 
     /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */
@@ -1865,8 +2321,8 @@ e1000_shift_in_mdi_bits(struct e1000_hw *hw)
 }
 
 /*****************************************************************************
-* Reads the value from a PHY register
-*
+* Reads the value from a PHY register, if the value is on a specific non zero
+* page, sets the page first.
 * hw - Struct containing variables accessed by shared code
 * reg_addr - address of the PHY register to read
 ******************************************************************************/
@@ -1874,12 +2330,34 @@ int32_t
 e1000_read_phy_reg(struct e1000_hw *hw,
                    uint32_t reg_addr,
                    uint16_t *phy_data)
+{
+    uint32_t ret_val;
+
+    DEBUGFUNC("e1000_read_phy_reg");
+
+    if(hw->phy_type == e1000_phy_igp &&
+       (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+        if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                             (uint16_t)reg_addr)))
+            return ret_val;
+    }
+
+    ret_val = e1000_read_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+                                    phy_data);
+
+    return ret_val;
+}
+
+int32_t
+e1000_read_phy_reg_ex(struct e1000_hw *hw,
+                      uint32_t reg_addr,
+                      uint16_t *phy_data)
 {
     uint32_t i;
     uint32_t mdic = 0;
     const uint32_t phy_addr = 1;
 
-    DEBUGFUNC("XXXXe1000_read_phy_reg");
+    DEBUGFUNC("e1000_read_phy_reg_ex");
 
     if(reg_addr > MAX_PHY_REG_ADDRESS) {
         DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
@@ -1892,14 +2370,14 @@ e1000_read_phy_reg(struct e1000_hw *hw,
          * PHY to retrieve the desired data.
          */
         mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) |
-                (phy_addr << E1000_MDIC_PHY_SHIFT) | 
+                (phy_addr << E1000_MDIC_PHY_SHIFT) |
                 (E1000_MDIC_OP_READ));
 
         E1000_WRITE_REG(hw, MDIC, mdic);
 
         /* Poll the ready bit to see if the MDI read completed */
         for(i = 0; i < 64; i++) {
-            udelay(10);
+            usec_delay(50);
             mdic = E1000_READ_REG(hw, MDIC);
             if(mdic & E1000_MDIC_READY) break;
         }
@@ -1930,7 +2408,7 @@ e1000_read_phy_reg(struct e1000_hw *hw,
          * READ operation is performed.  These two bits are thrown away
          * followed by a shift in of 16 bits which contains the desired data.
          */
-        mdic = ((reg_addr) | (phy_addr << 5) | 
+        mdic = ((reg_addr) | (phy_addr << 5) |
                 (PHY_OP_READ << 10) | (PHY_SOF << 12));
 
         e1000_shift_out_mdi_bits(hw, mdic, 14);
@@ -1941,7 +2419,7 @@ e1000_read_phy_reg(struct e1000_hw *hw,
          */
         *phy_data = e1000_shift_in_mdi_bits(hw);
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1955,12 +2433,34 @@ int32_t
 e1000_write_phy_reg(struct e1000_hw *hw,
                     uint32_t reg_addr,
                     uint16_t phy_data)
+{
+    uint32_t ret_val;
+
+    DEBUGFUNC("e1000_write_phy_reg");
+
+    if(hw->phy_type == e1000_phy_igp &&
+       (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+        if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                             (uint16_t)reg_addr)))
+            return ret_val;
+    }
+
+    ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+                                     phy_data);
+
+    return ret_val;
+}
+
+int32_t
+e1000_write_phy_reg_ex(struct e1000_hw *hw,
+                    uint32_t reg_addr,
+                    uint16_t phy_data)
 {
     uint32_t i;
     uint32_t mdic = 0;
     const uint32_t phy_addr = 1;
 
-    DEBUGFUNC("e1000_write_phy_reg");
+    DEBUGFUNC("e1000_write_phy_reg_ex");
 
     if(reg_addr > MAX_PHY_REG_ADDRESS) {
         DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
@@ -1974,14 +2474,14 @@ e1000_write_phy_reg(struct e1000_hw *hw,
          */
         mdic = (((uint32_t) phy_data) |
                 (reg_addr << E1000_MDIC_REG_SHIFT) |
-                (phy_addr << E1000_MDIC_PHY_SHIFT) | 
+                (phy_addr << E1000_MDIC_PHY_SHIFT) |
                 (E1000_MDIC_OP_WRITE));
 
         E1000_WRITE_REG(hw, MDIC, mdic);
 
         /* Poll the ready bit to see if the MDI read completed */
         for(i = 0; i < 64; i++) {
-            udelay(10);
+            usec_delay(50);
             mdic = E1000_READ_REG(hw, MDIC);
             if(mdic & E1000_MDIC_READY) break;
         }
@@ -1992,12 +2492,12 @@ e1000_write_phy_reg(struct e1000_hw *hw,
     } else {
         /* We'll need to use the SW defined pins to shift the write command
          * out to the PHY. We first send a preamble to the PHY to signal the
-         * beginning of the MII instruction.  This is done by sending 32 
+         * beginning of the MII instruction.  This is done by sending 32
          * consecutive "1" bits.
          */
         e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
 
-        /* Now combine the remaining required fields that will indicate a 
+        /* Now combine the remaining required fields that will indicate a
          * write operation. We use this method instead of calling the
          * e1000_shift_out_mdi_bits routine for each field in the command. The
          * format of a MII write instruction is as follows:
@@ -2010,7 +2510,8 @@ e1000_write_phy_reg(struct e1000_hw *hw,
 
         e1000_shift_out_mdi_bits(hw, mdic, 32);
     }
-    return 0;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2021,8 +2522,8 @@ e1000_write_phy_reg(struct e1000_hw *hw,
 void
 e1000_phy_hw_reset(struct e1000_hw *hw)
 {
-    uint32_t ctrl;
-    uint32_t ctrl_ext;
+    uint32_t ctrl, ctrl_ext;
+    uint32_t led_ctrl;
 
     DEBUGFUNC("e1000_phy_hw_reset");
 
@@ -2052,7 +2553,15 @@ e1000_phy_hw_reset(struct e1000_hw *hw)
         E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
         E1000_WRITE_FLUSH(hw);
     }
-    udelay(150);
+    usec_delay(150);
+
+    if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+        /* Configure activity LED after PHY reset */
+        led_ctrl = E1000_READ_REG(hw, LEDCTL);
+        led_ctrl &= IGP_ACTIVITY_LED_MASK;
+        led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+        E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+    }
 }
 
 /******************************************************************************
@@ -2065,21 +2574,26 @@ e1000_phy_hw_reset(struct e1000_hw *hw)
 int32_t
 e1000_phy_reset(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_phy_reset");
 
-    if(e1000_read_phy_reg(hw, PHY_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= MII_CR_RESET;
-    if(e1000_write_phy_reg(hw, PHY_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
-    udelay(1);
-    return 0;
+    if(hw->mac_type != e1000_82541_rev_2) {
+        if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+            return ret_val;
+
+        phy_data |= MII_CR_RESET;
+        if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+            return ret_val;
+
+        usec_delay(1);
+    } else e1000_phy_hw_reset(hw);
+
+    if(hw->phy_type == e1000_phy_igp)
+        e1000_phy_init_script(hw);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2090,22 +2604,21 @@ e1000_phy_reset(struct e1000_hw *hw)
 int32_t
 e1000_detect_gig_phy(struct e1000_hw *hw)
 {
+    int32_t phy_init_status, ret_val;
     uint16_t phy_id_high, phy_id_low;
     boolean_t match = FALSE;
 
     DEBUGFUNC("e1000_detect_gig_phy");
 
     /* Read the PHY ID Registers to identify which PHY is onboard. */
-    if(e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high)))
+        return ret_val;
+
     hw->phy_id = (uint32_t) (phy_id_high << 16);
-    udelay(2);
-    if(e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    usec_delay(20);
+    if((ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low)))
+        return ret_val;
+
     hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK);
     hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK;
 
@@ -2118,16 +2631,26 @@ e1000_detect_gig_phy(struct e1000_hw *hw)
         break;
     case e1000_82540:
     case e1000_82545:
+    case e1000_82545_rev_3:
     case e1000_82546:
+    case e1000_82546_rev_3:
         if(hw->phy_id == M88E1011_I_PHY_ID) match = TRUE;
         break;
+    case e1000_82541:
+    case e1000_82541_rev_2:
+    case e1000_82547:
+    case e1000_82547_rev_2:
+        if(hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE;
+        break;
     default:
         DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type);
         return -E1000_ERR_CONFIG;
     }
-    if(match) {
+    phy_init_status = e1000_set_phy_type(hw);
+
+    if ((match) && (phy_init_status == E1000_SUCCESS)) {
         DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id);
-        return 0;
+        return E1000_SUCCESS;
     }
     DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id);
     return -E1000_ERR_PHY;
@@ -2141,102 +2664,326 @@ e1000_detect_gig_phy(struct e1000_hw *hw)
 static int32_t
 e1000_phy_reset_dsp(struct e1000_hw *hw)
 {
-    int32_t ret_val = -E1000_ERR_PHY;
+    int32_t ret_val;
     DEBUGFUNC("e1000_phy_reset_dsp");
-    
+
     do {
-        if(e1000_write_phy_reg(hw, 29, 0x001d) < 0) break;
-        if(e1000_write_phy_reg(hw, 30, 0x00c1) < 0) break;
-        if(e1000_write_phy_reg(hw, 30, 0x0000) < 0) break;
-        ret_val = 0;
+        if((ret_val = e1000_write_phy_reg(hw, 29, 0x001d))) break;
+        if((ret_val = e1000_write_phy_reg(hw, 30, 0x00c1))) break;
+        if((ret_val = e1000_write_phy_reg(hw, 30, 0x0000))) break;
+        ret_val = E1000_SUCCESS;
     } while(0);
 
-    if(ret_val < 0) DEBUGOUT("PHY Write Error\n");
     return ret_val;
 }
 
 /******************************************************************************
-* Get PHY information from various PHY registers
+* Get PHY information from various PHY registers for igp PHY only.
 *
 * hw - Struct containing variables accessed by shared code
 * phy_info - PHY information structure
 ******************************************************************************/
 int32_t
-e1000_phy_get_info(struct e1000_hw *hw,
-                   struct e1000_phy_info *phy_info)
+e1000_phy_igp_get_info(struct e1000_hw *hw,
+                       struct e1000_phy_info *phy_info)
 {
-    int32_t ret_val = -E1000_ERR_PHY;
-    uint16_t phy_data;
+    int32_t ret_val;
+    uint16_t phy_data, polarity, min_length, max_length, average;
 
-    DEBUGFUNC("e1000_phy_get_info");
+    DEBUGFUNC("e1000_phy_igp_get_info");
 
-    phy_info->cable_length = e1000_cable_length_undefined;
-    phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined;
-    phy_info->cable_polarity = e1000_rev_polarity_undefined;
-    phy_info->polarity_correction = e1000_polarity_reversal_undefined;
-    phy_info->mdix_mode = e1000_auto_x_mode_undefined;
-    phy_info->local_rx = e1000_1000t_rx_status_undefined;
-    phy_info->remote_rx = e1000_1000t_rx_status_undefined;
+    /* The downshift status is checked only once, after link is established,
+     * and it stored in the hw->speed_downgraded parameter. */
+    phy_info->downshift = hw->speed_downgraded;
 
-    if(hw->media_type != e1000_media_type_copper) {
-        DEBUGOUT("PHY info is only valid for copper media\n");
-        return -E1000_ERR_CONFIG;
-    }
+    /* IGP01E1000 does not need to support it. */
+    phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal;
 
-    do {
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) break;
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) break;
-        if((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) {
-            DEBUGOUT("PHY info is only valid if link is up\n");
-            return -E1000_ERR_CONFIG;
-        }
+    /* IGP01E1000 always correct polarity reversal */
+    phy_info->polarity_correction = e1000_polarity_reversal_enabled;
 
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0)
-            break;
-        phy_info->extended_10bt_distance =
-            (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
-            M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT;
-        phy_info->polarity_correction =
-            (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >>
-            M88E1000_PSCR_POLARITY_REVERSAL_SHIFT;
-
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0)
-            break;
-        phy_info->cable_polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >>
-            M88E1000_PSSR_REV_POLARITY_SHIFT;
-        phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >>
-            M88E1000_PSSR_MDIX_SHIFT;
-        if(phy_data & M88E1000_PSSR_1000MBS) {
-            /* Cable Length Estimation and Local/Remote Receiver Informatoion
-             * are only valid at 1000 Mbps
-             */
-            phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
-                                      M88E1000_PSSR_CABLE_LENGTH_SHIFT);
-            if(e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data) < 0) 
-                break;
-            phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
-                SR_1000T_LOCAL_RX_STATUS_SHIFT;
-            phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >>
-                SR_1000T_REMOTE_RX_STATUS_SHIFT;
-        }
-        ret_val = 0;
-    } while(0);
+    /* Check polarity status */
+    if((ret_val = e1000_check_polarity(hw, &polarity)))
+        return ret_val;
 
-    if(ret_val < 0) DEBUGOUT("PHY Read Error\n");
-    return ret_val;
-}
+    phy_info->cable_polarity = polarity;
 
-int32_t
-e1000_validate_mdi_setting(struct e1000_hw *hw)
-{
-    DEBUGFUNC("e1000_validate_mdi_settings");
+    if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                     &phy_data)))
+        return ret_val;
 
-    if(!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) {
-        DEBUGOUT("Invalid MDI setting detected\n");
+    phy_info->mdix_mode = (phy_data & IGP01E1000_PSSR_MDIX) >>
+                          IGP01E1000_PSSR_MDIX_SHIFT;
+
+    if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+       IGP01E1000_PSSR_SPEED_1000MBPS) {
+        /* Local/Remote Receiver Information are only valid at 1000 Mbps */
+        if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data)))
+            return ret_val;
+
+        phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+                             SR_1000T_LOCAL_RX_STATUS_SHIFT;
+        phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+                              SR_1000T_REMOTE_RX_STATUS_SHIFT;
+
+        /* Get cable length */
+        if((ret_val = e1000_get_cable_length(hw, &min_length, &max_length)))
+            return ret_val;
+
+        /* transalte to old method */
+        average = (max_length + min_length) / 2;
+
+        if(average <= e1000_igp_cable_length_50)
+            phy_info->cable_length = e1000_cable_length_50;
+        else if(average <= e1000_igp_cable_length_80)
+            phy_info->cable_length = e1000_cable_length_50_80;
+        else if(average <= e1000_igp_cable_length_110)
+            phy_info->cable_length = e1000_cable_length_80_110;
+        else if(average <= e1000_igp_cable_length_140)
+            phy_info->cable_length = e1000_cable_length_110_140;
+        else
+            phy_info->cable_length = e1000_cable_length_140;
+    }
+
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers fot m88 PHY only.
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+int32_t
+e1000_phy_m88_get_info(struct e1000_hw *hw,
+                       struct e1000_phy_info *phy_info)
+{
+    int32_t ret_val;
+    uint16_t phy_data, polarity;
+
+    DEBUGFUNC("e1000_phy_m88_get_info");
+
+    /* The downshift status is checked only once, after link is established,
+     * and it stored in the hw->speed_downgraded parameter. */
+    phy_info->downshift = hw->speed_downgraded;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data)))
+        return ret_val;
+
+    phy_info->extended_10bt_distance =
+        (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
+        M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT;
+    phy_info->polarity_correction =
+        (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >>
+        M88E1000_PSCR_POLARITY_REVERSAL_SHIFT;
+
+    /* Check polarity status */
+    if((ret_val = e1000_check_polarity(hw, &polarity)))
+        return ret_val;
+
+    phy_info->cable_polarity = polarity;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data)))
+        return ret_val;
+
+    phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >>
+                          M88E1000_PSSR_MDIX_SHIFT;
+
+    if(phy_data & M88E1000_PSSR_1000MBS) {
+        /* Cable Length Estimation and Local/Remote Receiver Informatoion
+         * are only valid at 1000 Mbps
+         */
+        phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+                                  M88E1000_PSSR_CABLE_LENGTH_SHIFT);
+
+        if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data)))
+            return ret_val;
+
+        phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+                             SR_1000T_LOCAL_RX_STATUS_SHIFT;
+
+        phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+                              SR_1000T_REMOTE_RX_STATUS_SHIFT;
+    }
+
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+int32_t
+e1000_phy_get_info(struct e1000_hw *hw,
+                   struct e1000_phy_info *phy_info)
+{
+    int32_t ret_val;
+    uint16_t phy_data;
+
+    DEBUGFUNC("e1000_phy_get_info");
+
+    phy_info->cable_length = e1000_cable_length_undefined;
+    phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined;
+    phy_info->cable_polarity = e1000_rev_polarity_undefined;
+    phy_info->downshift = e1000_downshift_undefined;
+    phy_info->polarity_correction = e1000_polarity_reversal_undefined;
+    phy_info->mdix_mode = e1000_auto_x_mode_undefined;
+    phy_info->local_rx = e1000_1000t_rx_status_undefined;
+    phy_info->remote_rx = e1000_1000t_rx_status_undefined;
+
+    if(hw->media_type != e1000_media_type_copper) {
+        DEBUGOUT("PHY info is only valid for copper media\n");
+        return -E1000_ERR_CONFIG;
+    }
+
+    if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+        return ret_val;
+
+    if((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) {
+        DEBUGOUT("PHY info is only valid if link is up\n");
+        return -E1000_ERR_CONFIG;
+    }
+
+    if(hw->phy_type == e1000_phy_igp)
+        return e1000_phy_igp_get_info(hw, phy_info);
+    else
+        return e1000_phy_m88_get_info(hw, phy_info);
+}
+
+int32_t
+e1000_validate_mdi_setting(struct e1000_hw *hw)
+{
+    DEBUGFUNC("e1000_validate_mdi_settings");
+
+    if(!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) {
+        DEBUGOUT("Invalid MDI setting detected\n");
         hw->mdix = 1;
         return -E1000_ERR_CONFIG;
     }
-    return 0;
+    return E1000_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Sets up eeprom variables in the hw struct.  Must be called after mac_type
+ * is configured.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+void
+e1000_init_eeprom_params(struct e1000_hw *hw)
+{
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
+    uint32_t eecd = E1000_READ_REG(hw, EECD);
+    uint16_t eeprom_size;
+
+    DEBUGFUNC("e1000_init_eeprom_params");
+
+    switch (hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
+    case e1000_82544:
+        eeprom->type = e1000_eeprom_microwire;
+        eeprom->word_size = 64;
+        eeprom->opcode_bits = 3;
+        eeprom->address_bits = 6;
+        eeprom->delay_usec = 50;
+        break;
+    case e1000_82540:
+    case e1000_82545:
+    case e1000_82545_rev_3:
+    case e1000_82546:
+    case e1000_82546_rev_3:
+        eeprom->type = e1000_eeprom_microwire;
+        eeprom->opcode_bits = 3;
+        eeprom->delay_usec = 50;
+        if(eecd & E1000_EECD_SIZE) {
+            eeprom->word_size = 256;
+            eeprom->address_bits = 8;
+        } else {
+            eeprom->word_size = 64;
+            eeprom->address_bits = 6;
+        }
+        break;
+    case e1000_82541:
+    case e1000_82541_rev_2:
+    case e1000_82547:
+    case e1000_82547_rev_2:
+        if (eecd & E1000_EECD_TYPE) {
+            eeprom->type = e1000_eeprom_spi;
+            eeprom->opcode_bits = 8;
+            eeprom->delay_usec = 1;
+            if (eecd & E1000_EECD_ADDR_BITS) {
+                eeprom->page_size = 32;
+                eeprom->address_bits = 16;
+            } else {
+                eeprom->page_size = 8;
+                eeprom->address_bits = 8;
+            }
+        } else {
+            eeprom->type = e1000_eeprom_microwire;
+            eeprom->opcode_bits = 3;
+            eeprom->delay_usec = 50;
+            if (eecd & E1000_EECD_ADDR_BITS) {
+                eeprom->word_size = 256;
+                eeprom->address_bits = 8;
+            } else {
+                eeprom->word_size = 64;
+                eeprom->address_bits = 6;
+            }
+        }
+        break;
+    default:
+        eeprom->type = e1000_eeprom_spi;
+        eeprom->opcode_bits = 8;
+        eeprom->delay_usec = 1;
+        if (eecd & E1000_EECD_ADDR_BITS) {
+            eeprom->page_size = 32;
+            eeprom->address_bits = 16;
+        } else {
+            eeprom->page_size = 8;
+            eeprom->address_bits = 8;
+        }
+        break;
+    }
+
+    if (eeprom->type == e1000_eeprom_spi) {
+        eeprom->word_size = 64;
+        if (e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size) == 0) {
+            eeprom_size &= EEPROM_SIZE_MASK;
+
+            switch (eeprom_size) {
+            case EEPROM_SIZE_16KB:
+                eeprom->word_size = 8192;
+                break;
+            case EEPROM_SIZE_8KB:
+                eeprom->word_size = 4096;
+                break;
+            case EEPROM_SIZE_4KB:
+                eeprom->word_size = 2048;
+                break;
+            case EEPROM_SIZE_2KB:
+                eeprom->word_size = 1024;
+                break;
+            case EEPROM_SIZE_1KB:
+                eeprom->word_size = 512;
+                break;
+            case EEPROM_SIZE_512B:
+                eeprom->word_size = 256;
+                break;
+            case EEPROM_SIZE_128B:
+            default:
+                eeprom->word_size = 64;
+                break;
+            }
+        }
+    }
 }
 
 /******************************************************************************
@@ -2255,26 +3002,26 @@ e1000_raise_ee_clk(struct e1000_hw *hw,
     *eecd = *eecd | E1000_EECD_SK;
     E1000_WRITE_REG(hw, EECD, *eecd);
     E1000_WRITE_FLUSH(hw);
-    udelay(50);
+    usec_delay(hw->eeprom.delay_usec);
 }
 
 /******************************************************************************
  * Lowers the EEPROM's clock input.
  *
- * hw - Struct containing variables accessed by shared code 
+ * hw - Struct containing variables accessed by shared code
  * eecd - EECD's current value
  *****************************************************************************/
 static void
 e1000_lower_ee_clk(struct e1000_hw *hw,
                    uint32_t *eecd)
 {
-    /* Lower the clock input to the EEPROM (by clearing the SK bit), and then 
-     * wait 50 microseconds. 
+    /* Lower the clock input to the EEPROM (by clearing the SK bit), and then
+     * wait 50 microseconds.
      */
     *eecd = *eecd & ~E1000_EECD_SK;
     E1000_WRITE_REG(hw, EECD, *eecd);
     E1000_WRITE_FLUSH(hw);
-    udelay(50);
+    usec_delay(hw->eeprom.delay_usec);
 }
 
 /******************************************************************************
@@ -2289,16 +3036,21 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw,
                         uint16_t data,
                         uint16_t count)
 {
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
     uint32_t eecd;
     uint32_t mask;
 
     /* We need to shift "count" bits out to the EEPROM. So, value in the
      * "data" parameter will be shifted out to the EEPROM one bit at a time.
-     * In order to do this, "data" must be broken down into bits. 
+     * In order to do this, "data" must be broken down into bits.
      */
     mask = 0x01 << (count - 1);
     eecd = E1000_READ_REG(hw, EECD);
-    eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+    if (eeprom->type == e1000_eeprom_microwire) {
+        eecd &= ~E1000_EECD_DO;
+    } else if (eeprom->type == e1000_eeprom_spi) {
+        eecd |= E1000_EECD_DO;
+    }
     do {
         /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1",
          * and then raising and then lowering the clock (the SK bit controls
@@ -2313,7 +3065,7 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw,
         E1000_WRITE_REG(hw, EECD, eecd);
         E1000_WRITE_FLUSH(hw);
 
-        udelay(50);
+        usec_delay(eeprom->delay_usec);
 
         e1000_raise_ee_clk(hw, &eecd);
         e1000_lower_ee_clk(hw, &eecd);
@@ -2333,7 +3085,8 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw,
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
 static uint16_t
-e1000_shift_in_ee_bits(struct e1000_hw *hw)
+e1000_shift_in_ee_bits(struct e1000_hw *hw,
+                       uint16_t count)
 {
     uint32_t eecd;
     uint32_t i;
@@ -2351,7 +3104,7 @@ e1000_shift_in_ee_bits(struct e1000_hw *hw)
     eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
     data = 0;
 
-    for(i = 0; i < 16; i++) {
+    for(i = 0; i < count; i++) {
         data = data << 1;
         e1000_raise_ee_clk(hw, &eecd);
 
@@ -2372,104 +3125,196 @@ e1000_shift_in_ee_bits(struct e1000_hw *hw)
  *
  * hw - Struct containing variables accessed by shared code
  *
- * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This 
+ * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This
  * function should be called before issuing a command to the EEPROM.
  *****************************************************************************/
-static void
-e1000_setup_eeprom(struct e1000_hw *hw)
+static int32_t
+e1000_acquire_eeprom(struct e1000_hw *hw)
 {
-    uint32_t eecd;
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
+    uint32_t eecd, i=0;
+
+    DEBUGFUNC("e1000_acquire_eeprom");
 
     eecd = E1000_READ_REG(hw, EECD);
 
-    /* Clear SK and DI */
-    eecd &= ~(E1000_EECD_SK | E1000_EECD_DI);
-    E1000_WRITE_REG(hw, EECD, eecd);
+    /* Request EEPROM Access */
+    if(hw->mac_type > e1000_82544) {
+        eecd |= E1000_EECD_REQ;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        eecd = E1000_READ_REG(hw, EECD);
+        while((!(eecd & E1000_EECD_GNT)) &&
+              (i < E1000_EEPROM_GRANT_ATTEMPTS)) {
+            i++;
+            usec_delay(5);
+            eecd = E1000_READ_REG(hw, EECD);
+        }
+        if(!(eecd & E1000_EECD_GNT)) {
+            eecd &= ~E1000_EECD_REQ;
+            E1000_WRITE_REG(hw, EECD, eecd);
+            DEBUGOUT("Could not acquire EEPROM grant\n");
+            return -E1000_ERR_EEPROM;
+        }
+    }
 
-    /* Set CS */
-    eecd |= E1000_EECD_CS;
-    E1000_WRITE_REG(hw, EECD, eecd);
+    /* Setup EEPROM for Read/Write */
+
+    if (eeprom->type == e1000_eeprom_microwire) {
+        /* Clear SK and DI */
+        eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
+        E1000_WRITE_REG(hw, EECD, eecd);
+
+        /* Set CS */
+        eecd |= E1000_EECD_CS;
+        E1000_WRITE_REG(hw, EECD, eecd);
+    } else if (eeprom->type == e1000_eeprom_spi) {
+        /* Clear SK and CS */
+        eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+        E1000_WRITE_REG(hw, EECD, eecd);
+        usec_delay(1);
+    }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Returns EEPROM to a "standby" state
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
 static void
 e1000_standby_eeprom(struct e1000_hw *hw)
 {
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
     uint32_t eecd;
 
     eecd = E1000_READ_REG(hw, EECD);
 
-    /* Deselct EEPROM */
-    eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+    if(eeprom->type == e1000_eeprom_microwire) {
+        eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
 
-    /* Clock high */
-    eecd |= E1000_EECD_SK;
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+        /* Clock high */
+        eecd |= E1000_EECD_SK;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
 
-    /* Select EEPROM */
-    eecd |= E1000_EECD_CS;
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+        /* Select EEPROM */
+        eecd |= E1000_EECD_CS;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
 
-    /* Clock low */
-    eecd &= ~E1000_EECD_SK;
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+        /* Clock low */
+        eecd &= ~E1000_EECD_SK;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
+    } else if(eeprom->type == e1000_eeprom_spi) {
+        /* Toggle CS to flush commands */
+        eecd |= E1000_EECD_CS;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
+        eecd &= ~E1000_EECD_CS;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(eeprom->delay_usec);
+    }
 }
 
 /******************************************************************************
- * Raises then lowers the EEPROM's clock pin
+ * Terminates a command by inverting the EEPROM's chip select pin
  *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
 static void
-e1000_clock_eeprom(struct e1000_hw *hw)
+e1000_release_eeprom(struct e1000_hw *hw)
 {
     uint32_t eecd;
 
+    DEBUGFUNC("e1000_release_eeprom");
+
     eecd = E1000_READ_REG(hw, EECD);
 
-    /* Rising edge of clock */
-    eecd |= E1000_EECD_SK;
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+    if (hw->eeprom.type == e1000_eeprom_spi) {
+        eecd |= E1000_EECD_CS;  /* Pull CS high */
+        eecd &= ~E1000_EECD_SK; /* Lower SCK */
 
-    /* Falling edge of clock */
-    eecd &= ~E1000_EECD_SK;
-    E1000_WRITE_REG(hw, EECD, eecd);
-    E1000_WRITE_FLUSH(hw);
-    udelay(50);
+        E1000_WRITE_REG(hw, EECD, eecd);
+
+        usec_delay(hw->eeprom.delay_usec);
+    } else if(hw->eeprom.type == e1000_eeprom_microwire) {
+        /* cleanup eeprom */
+
+        /* CS on Microwire is active-high */
+        eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
+
+        E1000_WRITE_REG(hw, EECD, eecd);
+
+        /* Rising edge of clock */
+        eecd |= E1000_EECD_SK;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(hw->eeprom.delay_usec);
+
+        /* Falling edge of clock */
+        eecd &= ~E1000_EECD_SK;
+        E1000_WRITE_REG(hw, EECD, eecd);
+        E1000_WRITE_FLUSH(hw);
+        usec_delay(hw->eeprom.delay_usec);
+    }
+
+    /* Stop requesting EEPROM access */
+    if(hw->mac_type > e1000_82544) {
+        eecd &= ~E1000_EECD_REQ;
+        E1000_WRITE_REG(hw, EECD, eecd);
+    }
 }
 
 /******************************************************************************
- * Terminates a command by lowering the EEPROM's chip select pin
+ * Reads a 16 bit word from the EEPROM.
  *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
-static void
-e1000_cleanup_eeprom(struct e1000_hw *hw)
+int32_t
+e1000_spi_eeprom_ready(struct e1000_hw *hw)
 {
-    uint32_t eecd;
+    uint16_t retry_count = 0;
+    uint8_t spi_stat_reg;
 
-    eecd = E1000_READ_REG(hw, EECD);
+    DEBUGFUNC("e1000_spi_eeprom_ready");
 
-    eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
+    /* Read "Status Register" repeatedly until the LSB is cleared.  The
+     * EEPROM will signal that the command has been completed by clearing
+     * bit 0 of the internal status register.  If it's not cleared within
+     * 5 milliseconds, then error out.
+     */
+    retry_count = 0;
+    do {
+        e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
+                                hw->eeprom.opcode_bits);
+        spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8);
+        if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
+            break;
 
-    E1000_WRITE_REG(hw, EECD, eecd);
+        usec_delay(5);
+        retry_count += 5;
+
+    } while(retry_count < EEPROM_MAX_RETRY_SPI);
 
-    e1000_clock_eeprom(hw);
+    /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and
+     * only 0-5mSec on 5V devices)
+     */
+    if(retry_count >= EEPROM_MAX_RETRY_SPI) {
+        DEBUGOUT("SPI EEPROM Status error\n");
+        return -E1000_ERR_EEPROM;
+    }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2477,71 +3322,85 @@ e1000_cleanup_eeprom(struct e1000_hw *hw)
  *
  * hw - Struct containing variables accessed by shared code
  * offset - offset of  word in the EEPROM to read
- * data - word read from the EEPROM 
+ * data - word read from the EEPROM
+ * words - number of words to read
  *****************************************************************************/
 int32_t
 e1000_read_eeprom(struct e1000_hw *hw,
                   uint16_t offset,
+                  uint16_t words,
                   uint16_t *data)
 {
-    uint32_t eecd;
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
     uint32_t i = 0;
-    boolean_t large_eeprom = FALSE;
 
     DEBUGFUNC("e1000_read_eeprom");
 
-    /* Request EEPROM Access */
-    if(hw->mac_type > e1000_82544) {
-        eecd = E1000_READ_REG(hw, EECD);
-        if(eecd & E1000_EECD_SIZE) large_eeprom = TRUE;
-        eecd |= E1000_EECD_REQ;
-        E1000_WRITE_REG(hw, EECD, eecd);
-        eecd = E1000_READ_REG(hw, EECD);
-        while((!(eecd & E1000_EECD_GNT)) && (i < 100)) {
-            i++;
-            udelay(5);
-            eecd = E1000_READ_REG(hw, EECD);
-        }
-        if(!(eecd & E1000_EECD_GNT)) {
-            eecd &= ~E1000_EECD_REQ;
-            E1000_WRITE_REG(hw, EECD, eecd);
-            DEBUGOUT("Could not acquire EEPROM grant\n");
-            return -E1000_ERR_EEPROM;
-        }
+    /* A check for invalid values:  offset too large, too many words, and not
+     * enough words.
+     */
+    if((offset > eeprom->word_size) || (words > eeprom->word_size - offset) ||
+       (words == 0)) {
+        DEBUGOUT("\"words\" parameter out of bounds\n");
+        return -E1000_ERR_EEPROM;
     }
 
-    /*  Prepare the EEPROM for reading  */
-    e1000_setup_eeprom(hw);
+    /* Prepare the EEPROM for reading  */
+    if(e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+        return -E1000_ERR_EEPROM;
 
-    /*  Send the READ command (opcode + addr)  */
-    e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE, 3);
-    if(large_eeprom) {
-        /* If we have a 256 word EEPROM, there are 8 address bits */
-        e1000_shift_out_ee_bits(hw, offset, 8);
-    } else {
-        /* If we have a 64 word EEPROM, there are 6 address bits */
-        e1000_shift_out_ee_bits(hw, offset, 6);
-    }
+    if(eeprom->type == e1000_eeprom_spi) {
+        uint16_t word_in;
+        uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
 
-    /* Read the data */
-    *data = e1000_shift_in_ee_bits(hw);
+        if(e1000_spi_eeprom_ready(hw)) {
+            e1000_release_eeprom(hw);
+            return -E1000_ERR_EEPROM;
+        }
 
-    /* End this read operation */
-    e1000_standby_eeprom(hw);
+        e1000_standby_eeprom(hw);
 
-    /* Stop requesting EEPROM access */
-    if(hw->mac_type > e1000_82544) {
-        eecd = E1000_READ_REG(hw, EECD);
-        eecd &= ~E1000_EECD_REQ;
-        E1000_WRITE_REG(hw, EECD, eecd);
+        /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+        if((eeprom->address_bits == 8) && (offset >= 128))
+            read_opcode |= EEPROM_A8_OPCODE_SPI;
+
+        /* Send the READ command (opcode + addr)  */
+        e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
+        e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
+
+        /* Read the data.  The address of the eeprom internally increments with
+         * each byte (spi) being read, saving on the overhead of eeprom setup
+         * and tear-down.  The address counter will roll over if reading beyond
+         * the size of the eeprom, thus allowing the entire memory to be read
+         * starting from any offset. */
+        for (i = 0; i < words; i++) {
+            word_in = e1000_shift_in_ee_bits(hw, 16);
+            data[i] = (word_in >> 8) | (word_in << 8);
+        }
+    } else if(eeprom->type == e1000_eeprom_microwire) {
+        for (i = 0; i < words; i++) {
+            /* Send the READ command (opcode + addr)  */
+            e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
+                                    eeprom->opcode_bits);
+            e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
+                                    eeprom->address_bits);
+
+            /* Read the data.  For microwire, each word requires the overhead
+             * of eeprom setup and tear-down. */
+            data[i] = e1000_shift_in_ee_bits(hw, 16);
+            e1000_standby_eeprom(hw);
+        }
     }
 
-    return 0;
+    /* End this read operation */
+    e1000_release_eeprom(hw);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Verifies that the EEPROM has a valid checksum
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  *
  * Reads the first 64 16 bit words of the EEPROM and sums the values read.
@@ -2557,17 +3416,17 @@ e1000_validate_eeprom_checksum(struct e1000_hw *hw)
     DEBUGFUNC("e1000_validate_eeprom_checksum");
 
     for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
-        if(e1000_read_eeprom(hw, i, &eeprom_data) < 0) {
+        if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
             DEBUGOUT("EEPROM Read Error\n");
             return -E1000_ERR_EEPROM;
         }
         checksum += eeprom_data;
     }
 
-    if(checksum == (uint16_t) EEPROM_SUM) {
-        return 0;
-    else {
-        DEBUGOUT("EEPROM Checksum Invalid\n");    
+    if(checksum == (uint16_t) EEPROM_SUM)
+        return E1000_SUCCESS;
+    else {
+        DEBUGOUT("EEPROM Checksum Invalid\n");
         return -E1000_ERR_EEPROM;
     }
 }
@@ -2589,133 +3448,216 @@ e1000_update_eeprom_checksum(struct e1000_hw *hw)
     DEBUGFUNC("e1000_update_eeprom_checksum");
 
     for(i = 0; i < EEPROM_CHECKSUM_REG; i++) {
-        if(e1000_read_eeprom(hw, i, &eeprom_data) < 0) {
+        if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
             DEBUGOUT("EEPROM Read Error\n");
             return -E1000_ERR_EEPROM;
         }
         checksum += eeprom_data;
     }
     checksum = (uint16_t) EEPROM_SUM - checksum;
-    if(e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, checksum) < 0) {
+    if(e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) {
         DEBUGOUT("EEPROM Write Error\n");
         return -E1000_ERR_EEPROM;
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
- * Writes a 16 bit word to a given offset in the EEPROM.
+ * Parent function for writing words to the different EEPROM types.
  *
  * hw - Struct containing variables accessed by shared code
  * offset - offset within the EEPROM to be written to
- * data - 16 bit word to be writen to the EEPROM
+ * words - number of words to write
+ * data - 16 bit word to be written to the EEPROM
  *
- * If e1000_update_eeprom_checksum is not called after this function, the 
+ * If e1000_update_eeprom_checksum is not called after this function, the
  * EEPROM will most likely contain an invalid checksum.
  *****************************************************************************/
 int32_t
 e1000_write_eeprom(struct e1000_hw *hw,
                    uint16_t offset,
-                   uint16_t data)
+                   uint16_t words,
+                   uint16_t *data)
 {
-    uint32_t eecd;
-    uint32_t i = 0;
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
     int32_t status = 0;
-    boolean_t large_eeprom = FALSE;
 
     DEBUGFUNC("e1000_write_eeprom");
 
-    /* Request EEPROM Access */
-    if(hw->mac_type > e1000_82544) {
-        eecd = E1000_READ_REG(hw, EECD);
-        if(eecd & E1000_EECD_SIZE) large_eeprom = TRUE;
-        eecd |= E1000_EECD_REQ;
-        E1000_WRITE_REG(hw, EECD, eecd);
-        eecd = E1000_READ_REG(hw, EECD);
-        while((!(eecd & E1000_EECD_GNT)) && (i < 100)) {
-            i++;
-            udelay(5);
-            eecd = E1000_READ_REG(hw, EECD);
-        }
-        if(!(eecd & E1000_EECD_GNT)) {
-            eecd &= ~E1000_EECD_REQ;
-            E1000_WRITE_REG(hw, EECD, eecd);
-            DEBUGOUT("Could not acquire EEPROM grant\n");
-            return -E1000_ERR_EEPROM;
-        }
+    /* A check for invalid values:  offset too large, too many words, and not
+     * enough words.
+     */
+    if((offset > eeprom->word_size) || (words > eeprom->word_size - offset) ||
+       (words == 0)) {
+        DEBUGOUT("\"words\" parameter out of bounds\n");
+        return -E1000_ERR_EEPROM;
     }
 
     /* Prepare the EEPROM for writing  */
-    e1000_setup_eeprom(hw);
+    if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+        return -E1000_ERR_EEPROM;
 
-    /* Send the 9-bit (or 11-bit on large EEPROM) EWEN (write enable) command
-     * to the EEPROM (5-bit opcode plus 4/6-bit dummy). This puts the EEPROM
-     * into write/erase mode. 
-     */
-    e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE, 5);
-    if(large_eeprom) 
-        e1000_shift_out_ee_bits(hw, 0, 6);
+    if(eeprom->type == e1000_eeprom_microwire)
+        status = e1000_write_eeprom_microwire(hw, offset, words, data);
     else
-        e1000_shift_out_ee_bits(hw, 0, 4);
+        status = e1000_write_eeprom_spi(hw, offset, words, data);
 
-    /* Prepare the EEPROM */
-    e1000_standby_eeprom(hw);
+    /* Done with writing */
+    e1000_release_eeprom(hw);
 
-    /* Send the Write command (3-bit opcode + addr) */
-    e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE, 3);
-    if(large_eeprom) 
-        /* If we have a 256 word EEPROM, there are 8 address bits */
-        e1000_shift_out_ee_bits(hw, offset, 8);
-    else
-        /* If we have a 64 word EEPROM, there are 6 address bits */
-        e1000_shift_out_ee_bits(hw, offset, 6);
+    return status;
+}
 
-    /* Send the data */
-    e1000_shift_out_ee_bits(hw, data, 16);
+/******************************************************************************
+ * Writes a 16 bit word to a given offset in an SPI EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset within the EEPROM to be written to
+ * words - number of words to write
+ * data - pointer to array of 8 bit words to be written to the EEPROM
+ *
+ *****************************************************************************/
+int32_t
+e1000_write_eeprom_spi(struct e1000_hw *hw,
+                       uint16_t offset,
+                       uint16_t words,
+                       uint16_t *data)
+{
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
+    uint16_t widx = 0;
 
-    /* Toggle the CS line.  This in effect tells to EEPROM to actually execute 
-     * the command in question.
-     */
-    e1000_standby_eeprom(hw);
+    DEBUGFUNC("e1000_write_eeprom_spi");
 
-    /* Now read DO repeatedly until is high (equal to '1').  The EEEPROM will
-     * signal that the command has been completed by raising the DO signal.
-     * If DO does not go high in 10 milliseconds, then error out.
-     */
-    for(i = 0; i < 200; i++) {
-        eecd = E1000_READ_REG(hw, EECD);
-        if(eecd & E1000_EECD_DO) break;
-        udelay(50);
-    }
-    if(i == 200) {
-        DEBUGOUT("EEPROM Write did not complete\n");
-        status = -E1000_ERR_EEPROM;
+    while (widx < words) {
+        uint8_t write_opcode = EEPROM_WRITE_OPCODE_SPI;
+
+        if(e1000_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM;
+
+        e1000_standby_eeprom(hw);
+
+        /*  Send the WRITE ENABLE command (8 bit opcode )  */
+        e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI,
+                                    eeprom->opcode_bits);
+
+        e1000_standby_eeprom(hw);
+
+        /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+        if((eeprom->address_bits == 8) && (offset >= 128))
+            write_opcode |= EEPROM_A8_OPCODE_SPI;
+
+        /* Send the Write command (8-bit opcode + addr) */
+        e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits);
+
+        e1000_shift_out_ee_bits(hw, (uint16_t)((offset + widx)*2),
+                                eeprom->address_bits);
+
+        /* Send the data */
+
+        /* Loop to allow for up to whole page write (32 bytes) of eeprom */
+        while (widx < words) {
+            uint16_t word_out = data[widx];
+            word_out = (word_out >> 8) | (word_out << 8);
+            e1000_shift_out_ee_bits(hw, word_out, 16);
+            widx++;
+
+            /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE
+             * operation, while the smaller eeproms are capable of an 8-byte
+             * PAGE WRITE operation.  Break the inner loop to pass new address
+             */
+            if((((offset + widx)*2) % eeprom->page_size) == 0) {
+                e1000_standby_eeprom(hw);
+                break;
+            }
+        }
     }
 
-    /* Recover from write */
-    e1000_standby_eeprom(hw);
+    return E1000_SUCCESS;
+}
 
-    /* Send the 9-bit (or 11-bit on large EEPROM) EWDS (write disable) command
-     * to the EEPROM (5-bit opcode plus 4/6-bit dummy). This takes the EEPROM
-     * out of write/erase mode.
+/******************************************************************************
+ * Writes a 16 bit word to a given offset in a Microwire EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset within the EEPROM to be written to
+ * words - number of words to write
+ * data - pointer to array of 16 bit words to be written to the EEPROM
+ *
+ *****************************************************************************/
+int32_t
+e1000_write_eeprom_microwire(struct e1000_hw *hw,
+                             uint16_t offset,
+                             uint16_t words,
+                             uint16_t *data)
+{
+    struct e1000_eeprom_info *eeprom = &hw->eeprom;
+    uint32_t eecd;
+    uint16_t words_written = 0;
+    uint16_t i = 0;
+
+    DEBUGFUNC("e1000_write_eeprom_microwire");
+
+    /* Send the write enable command to the EEPROM (3-bit opcode plus
+     * 6/8-bit dummy address beginning with 11).  It's less work to include
+     * the 11 of the dummy address as part of the opcode than it is to shift
+     * it over the correct number of bits for the address.  This puts the
+     * EEPROM into write/erase mode.
      */
-    e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE, 5);
-    if(large_eeprom) 
-        e1000_shift_out_ee_bits(hw, 0, 6);
-    else
-        e1000_shift_out_ee_bits(hw, 0, 4);
+    e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE,
+                            (uint16_t)(eeprom->opcode_bits + 2));
 
-    /* Done with writing */
-    e1000_cleanup_eeprom(hw);
+    e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2));
 
-    /* Stop requesting EEPROM access */
-    if(hw->mac_type > e1000_82544) {
-        eecd = E1000_READ_REG(hw, EECD);
-        eecd &= ~E1000_EECD_REQ;
-        E1000_WRITE_REG(hw, EECD, eecd);
+    /* Prepare the EEPROM */
+    e1000_standby_eeprom(hw);
+
+    while (words_written < words) {
+        /* Send the Write command (3-bit opcode + addr) */
+        e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE,
+                                eeprom->opcode_bits);
+
+        e1000_shift_out_ee_bits(hw, (uint16_t)(offset + words_written),
+                                eeprom->address_bits);
+
+        /* Send the data */
+        e1000_shift_out_ee_bits(hw, data[words_written], 16);
+
+        /* Toggle the CS line.  This in effect tells the EEPROM to execute
+         * the previous command.
+         */
+        e1000_standby_eeprom(hw);
+
+        /* Read DO repeatedly until it is high (equal to '1').  The EEPROM will
+         * signal that the command has been completed by raising the DO signal.
+         * If DO does not go high in 10 milliseconds, then error out.
+         */
+        for(i = 0; i < 200; i++) {
+            eecd = E1000_READ_REG(hw, EECD);
+            if(eecd & E1000_EECD_DO) break;
+            usec_delay(50);
+        }
+        if(i == 200) {
+            DEBUGOUT("EEPROM Write did not complete\n");
+            return -E1000_ERR_EEPROM;
+        }
+
+        /* Recover from write */
+        e1000_standby_eeprom(hw);
+
+        words_written++;
     }
 
-    return status;
+    /* Send the write disable command to the EEPROM (3-bit opcode plus
+     * 6/8-bit dummy address beginning with 10).  It's less work to include
+     * the 10 of the dummy address as part of the opcode than it is to shift
+     * it over the correct number of bits for the address.  This takes the
+     * EEPROM out of write/erase mode.
+     */
+    e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE,
+                            (uint16_t)(eeprom->opcode_bits + 2));
+
+    e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2));
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2734,7 +3676,7 @@ e1000_read_part_num(struct e1000_hw *hw,
     DEBUGFUNC("e1000_read_part_num");
 
     /* Get word 0 from EEPROM */
-    if(e1000_read_eeprom(hw, offset, &eeprom_data) < 0) {
+    if(e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
         DEBUGOUT("EEPROM Read Error\n");
         return -E1000_ERR_EEPROM;
     }
@@ -2742,14 +3684,14 @@ e1000_read_part_num(struct e1000_hw *hw,
     *part_num = (uint32_t) (eeprom_data << 16);
 
     /* Get word 1 from EEPROM */
-    if(e1000_read_eeprom(hw, ++offset, &eeprom_data) < 0) {
+    if(e1000_read_eeprom(hw, ++offset, 1, &eeprom_data) < 0) {
         DEBUGOUT("EEPROM Read Error\n");
         return -E1000_ERR_EEPROM;
     }
     /* Save word 1 in lower half of part_num */
     *part_num |= eeprom_data;
 
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2768,14 +3710,14 @@ e1000_read_mac_addr(struct e1000_hw * hw)
 
     for(i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
         offset = i >> 1;
-        if(e1000_read_eeprom(hw, offset, &eeprom_data) < 0) {
+        if(e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
             DEBUGOUT("EEPROM Read Error\n");
             return -E1000_ERR_EEPROM;
         }
         hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF);
         hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8);
     }
-    if((hw->mac_type == e1000_82546) &&
+    if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) &&
        (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         if(hw->perm_mac_addr[5] & 0x01)
             hw->perm_mac_addr[5] &= ~(0x01);
@@ -2784,13 +3726,13 @@ e1000_read_mac_addr(struct e1000_hw * hw)
     }
     for(i = 0; i < NODE_ADDRESS_SIZE; i++)
         hw->mac_addr[i] = hw->perm_mac_addr[i];
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
  * Initializes receive address filters.
  *
- * hw - Struct containing variables accessed by shared code 
+ * hw - Struct containing variables accessed by shared code
  *
  * Places the MAC address in receive address register 0 and clears the rest
  * of the receive addresss registers. Clears the multicast table. Assumes
@@ -2835,7 +3777,7 @@ e1000_init_rx_addrs(struct e1000_hw *hw)
  *
  * The given list replaces any existing list. Clears the last 15 receive
  * address registers and the multicast table. Uses receive address registers
- * for the first 15 multicast addresses, and hashes the rest into the 
+ * for the first 15 multicast addresses, and hashes the rest into the
  * multicast table.
  *****************************************************************************/
 void
@@ -2884,7 +3826,7 @@ e1000_mc_addr_list_update(struct e1000_hw *hw,
         DEBUGOUT1(" Hash value = 0x%03X\n", hash_value);
 
         /* Place this multicast address in the RAR if there is room, *
-         * else put it in the MTA            
+         * else put it in the MTA
          */
         if(rar_used_count < E1000_RAR_ENTRIES) {
             e1000_rar_set(hw,
@@ -2902,7 +3844,7 @@ e1000_mc_addr_list_update(struct e1000_hw *hw,
  * Hashes an address to determine its location in the multicast table
  *
  * hw - Struct containing variables accessed by shared code
- * mc_addr - the multicast address to hash 
+ * mc_addr - the multicast address to hash
  *****************************************************************************/
 uint32_t
 e1000_hash_mc_addr(struct e1000_hw *hw,
@@ -2911,7 +3853,7 @@ e1000_hash_mc_addr(struct e1000_hw *hw,
     uint32_t hash_value = 0;
 
     /* The portion of the address that is used for the hash table is
-     * determined by the mc_filter_type setting.  
+     * determined by the mc_filter_type setting.
      */
     switch (hw->mc_filter_type) {
     /* [0] [1] [2] [3] [4] [5]
@@ -2954,12 +3896,12 @@ e1000_mta_set(struct e1000_hw *hw,
     uint32_t mta;
     uint32_t temp;
 
-    /* The MTA is a register array of 128 32-bit registers.  
-     * It is treated like an array of 4096 bits.  We want to set 
+    /* The MTA is a register array of 128 32-bit registers.
+     * It is treated like an array of 4096 bits.  We want to set
      * bit BitArray[hash_value]. So we figure out what register
      * the bit is in, read it, OR in the new bit, then write
-     * back the new value.  The register is determined by the 
-     * upper 7 bits of the hash value and the bit within that 
+     * back the new value.  The register is determined by the
+     * upper 7 bits of the hash value and the bit within that
      * register are determined by the lower 5 bits of the value.
      */
     hash_reg = (hash_value >> 5) & 0x7F;
@@ -2997,7 +3939,7 @@ e1000_rar_set(struct e1000_hw *hw,
     uint32_t rar_low, rar_high;
 
     /* HW expects these in little endian so we reverse the byte order
-     * from network order (big endian) to little endian              
+     * from network order (big endian) to little endian
      */
     rar_low = ((uint32_t) addr[0] |
                ((uint32_t) addr[1] << 8) |
@@ -3055,24 +3997,24 @@ e1000_id_led_init(struct e1000_hw * hw)
     const uint32_t ledctl_off = E1000_LEDCTL_MODE_LED_OFF;
     uint16_t eeprom_data, i, temp;
     const uint16_t led_mask = 0x0F;
-        
+
     DEBUGFUNC("e1000_id_led_init");
-    
+
     if(hw->mac_type < e1000_82540) {
         /* Nothing to do */
-        return 0;
+        return E1000_SUCCESS;
     }
-    
+
     ledctl = E1000_READ_REG(hw, LEDCTL);
     hw->ledctl_default = ledctl;
     hw->ledctl_mode1 = hw->ledctl_default;
     hw->ledctl_mode2 = hw->ledctl_default;
-        
-    if(e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, &eeprom_data) < 0) {
+
+    if(e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) {
         DEBUGOUT("EEPROM Read Error\n");
         return -E1000_ERR_EEPROM;
     }
-    if((eeprom_data== ID_LED_RESERVED_0000) || 
+    if((eeprom_data== ID_LED_RESERVED_0000) ||
        (eeprom_data == ID_LED_RESERVED_FFFF)) eeprom_data = ID_LED_DEFAULT;
     for(i = 0; i < 4; i++) {
         temp = (eeprom_data >> (i << 2)) & led_mask;
@@ -3111,7 +4053,7 @@ e1000_id_led_init(struct e1000_hw * hw)
             break;
         }
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3123,45 +4065,48 @@ int32_t
 e1000_setup_led(struct e1000_hw *hw)
 {
     uint32_t ledctl;
+    int32_t ret_val = E1000_SUCCESS;
+
     DEBUGFUNC("e1000_setup_led");
-   
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
+
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
+    case e1000_82544:
         /* No setup necessary */
         break;
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ledctl = E1000_READ_REG(hw, LEDCTL);
-        /* Save current LEDCTL settings */
-        hw->ledctl_default = ledctl;
-        /* Turn off LED0 */
-        ledctl &= ~(E1000_LEDCTL_LED0_IVRT |
-                    E1000_LEDCTL_LED0_BLINK | 
-                    E1000_LEDCTL_LED0_MODE_MASK);
-        ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT);
-        E1000_WRITE_REG(hw, LEDCTL, ledctl);
-        break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
-        break;
+    case e1000_82541:
+    case e1000_82547:
+    case e1000_82541_rev_2:
+    case e1000_82547_rev_2:
+        /* Turn off PHY Smart Power Down (if enabled) */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                         &hw->phy_spd_default)))
+            return ret_val;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                          (uint16_t)(hw->phy_spd_default &
+                                          ~IGP01E1000_GMII_SPD))))
+            return ret_val;
+        /* Fall Through */
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            ledctl = E1000_READ_REG(hw, LEDCTL);
+            /* Save current LEDCTL settings */
+            hw->ledctl_default = ledctl;
+            /* Turn off LED0 */
+            ledctl &= ~(E1000_LEDCTL_LED0_IVRT |
+                        E1000_LEDCTL_LED0_BLINK |
+                        E1000_LEDCTL_LED0_MODE_MASK);
+            ledctl |= (E1000_LEDCTL_MODE_LED_OFF <<
+                       E1000_LEDCTL_LED0_MODE_SHIFT);
+            E1000_WRITE_REG(hw, LEDCTL, ledctl);
+        } else if(hw->media_type == e1000_media_type_copper)
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+        break;
     }
-    return 0;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3172,37 +4117,35 @@ e1000_setup_led(struct e1000_hw *hw)
 int32_t
 e1000_cleanup_led(struct e1000_hw *hw)
 {
+    int32_t ret_val = E1000_SUCCESS;
+
     DEBUGFUNC("e1000_cleanup_led");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
+    case e1000_82544:
         /* No cleanup necessary */
         break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_COPPER:
-    case E1000_DEV_ID_82546EB_FIBER:
+    case e1000_82541:
+    case e1000_82547:
+    case e1000_82541_rev_2:
+    case e1000_82547_rev_2:
+        /* Turn on PHY Smart Power Down (if previously enabled) */
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                          hw->phy_spd_default)))
+            return ret_val;
+        /* Fall Through */
+    default:
         /* Restore LEDCTL settings */
         E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default);
         break;
-    default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
     }
-    return 0;
+
+    return E1000_SUCCESS;
 }
-    
+
 /******************************************************************************
  * Turns on the software controllable LED
  *
@@ -3211,46 +4154,44 @@ e1000_cleanup_led(struct e1000_hw *hw)
 int32_t
 e1000_led_on(struct e1000_hw *hw)
 {
-    uint32_t ctrl;
+    uint32_t ctrl = E1000_READ_REG(hw, CTRL);
 
     DEBUGFUNC("e1000_led_on");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
         /* Set SW Defineable Pin 0 to turn on the LED */
         ctrl |= E1000_CTRL_SWDPIN0;
         ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
-        break;
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
-        /* Clear SW Defineable Pin 0 to turn on the LED */
-        ctrl &= ~E1000_CTRL_SWDPIN0;
-        ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
         break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2);
+    case e1000_82544:
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Set SW Defineable Pin 0 to turn on the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else {
+            /* Clear SW Defineable Pin 0 to turn on the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        }
         break;
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Clear SW Defineable Pin 0 to turn on the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else if(hw->media_type == e1000_media_type_copper) {
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2);
+            return E1000_SUCCESS;
+        }
+        break;
     }
-    return 0;
+
+    E1000_WRITE_REG(hw, CTRL, ctrl);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3261,50 +4202,48 @@ e1000_led_on(struct e1000_hw *hw)
 int32_t
 e1000_led_off(struct e1000_hw *hw)
 {
-    uint32_t ctrl;
+    uint32_t ctrl = E1000_READ_REG(hw, CTRL);
 
     DEBUGFUNC("e1000_led_off");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
         /* Clear SW Defineable Pin 0 to turn off the LED */
         ctrl &= ~E1000_CTRL_SWDPIN0;
         ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
-        break;
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
-        /* Set SW Defineable Pin 0 to turn off the LED */
-        ctrl |= E1000_CTRL_SWDPIN0;
-        ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
         break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+    case e1000_82544:
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Clear SW Defineable Pin 0 to turn off the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else {
+            /* Set SW Defineable Pin 0 to turn off the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        }
         break;
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Set SW Defineable Pin 0 to turn off the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else if(hw->media_type == e1000_media_type_copper) {
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+            return E1000_SUCCESS;
+        }
+        break;
     }
-    return 0;
+
+    E1000_WRITE_REG(hw, CTRL, ctrl);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
- * Clears all hardware statistics counters. 
+ * Clears all hardware statistics counters.
  *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
@@ -3423,8 +4362,7 @@ e1000_update_adaptive(struct e1000_hw *hw)
     DEBUGFUNC("e1000_update_adaptive");
 
     if(hw->adaptive_ifs) {
-        if((hw->collision_delta * hw->ifs_ratio) > 
-           hw->tx_packet_delta) {
+        if((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) {
             if(hw->tx_packet_delta > MIN_NUM_XMITS) {
                 hw->in_ifs_mode = TRUE;
                 if(hw->current_ifs_val < hw->ifs_max_val) {
@@ -3436,8 +4374,7 @@ e1000_update_adaptive(struct e1000_hw *hw)
                 }
             }
         } else {
-            if((hw->in_ifs_mode == TRUE) && 
-               (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
+            if(hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
                 hw->current_ifs_val = 0;
                 hw->in_ifs_mode = FALSE;
                 E1000_WRITE_REG(hw, AIT, 0);
@@ -3450,7 +4387,7 @@ e1000_update_adaptive(struct e1000_hw *hw)
 
 /******************************************************************************
  * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT
- * 
+ *
  * hw - Struct containing variables accessed by shared code
  * frame_len - The length of the frame in question
  * mac_addr - The Ethernet destination address of the frame in question
@@ -3478,16 +4415,16 @@ e1000_tbi_adjust_stats(struct e1000_hw *hw,
     carry_bit = 0x80000000 & stats->gorcl;
     stats->gorcl += frame_len;
     /* If the high bit of Gorcl (the low 32 bits of the Good Octets
-     * Received Count) was one before the addition, 
-     * AND it is zero after, then we lost the carry out, 
+     * Received Count) was one before the addition,
+     * AND it is zero after, then we lost the carry out,
      * need to add one to Gorch (Good Octets Received Count High).
-     * This could be simplified if all environments supported 
+     * This could be simplified if all environments supported
      * 64-bit integers.
      */
     if(carry_bit && ((stats->gorcl & 0x80000000) == 0))
         stats->gorch++;
     /* Is this a broadcast or multicast?  Check broadcast first,
-     * since the test for a multicast frame will test positive on 
+     * since the test for a multicast frame will test positive on
      * a broadcast frame.
      */
     if((mac_addr[0] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff))
@@ -3548,7 +4485,11 @@ e1000_get_bus_info(struct e1000_hw *hw)
     status = E1000_READ_REG(hw, STATUS);
     hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ?
                    e1000_bus_type_pcix : e1000_bus_type_pci;
-    if(hw->bus_type == e1000_bus_type_pci) {
+
+    if(hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) {
+        hw->bus_speed = (hw->bus_type == e1000_bus_type_pci) ?
+                        e1000_bus_speed_66 : e1000_bus_speed_120;
+    } else if(hw->bus_type == e1000_bus_type_pci) {
         hw->bus_speed = (status & E1000_STATUS_PCI66) ?
                         e1000_bus_speed_66 : e1000_bus_speed_33;
     } else {
@@ -3608,3 +4549,547 @@ e1000_write_reg_io(struct e1000_hw *hw,
     e1000_io_write(hw, io_data, value);
 }
 
+
+/******************************************************************************
+ * Estimates the cable length.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * min_length - The estimated minimum length
+ * max_length - The estimated maximum length
+ *
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS
+ *
+ * This function always returns a ranged length (minimum & maximum).
+ * So for M88 phy's, this function interprets the one value returned from the
+ * register to the minimum and maximum range.
+ * For IGP phy's, the function calculates the range by the AGC registers.
+ *****************************************************************************/
+int32_t
+e1000_get_cable_length(struct e1000_hw *hw,
+                       uint16_t *min_length,
+                       uint16_t *max_length)
+{
+    int32_t ret_val;
+    uint16_t agc_value = 0;
+    uint16_t cur_agc, min_agc = IGP01E1000_AGC_LENGTH_TABLE_SIZE;
+    uint16_t i, phy_data;
+
+    DEBUGFUNC("e1000_get_cable_length");
+
+    *min_length = *max_length = 0;
+
+    /* Use old method for Phy older than IGP */
+    if(hw->phy_type == e1000_phy_m88) {
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
+        /* Convert the enum value to ranged values */
+        switch((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+               M88E1000_PSSR_CABLE_LENGTH_SHIFT) {
+        case e1000_cable_length_50:
+            *min_length = 0;
+            *max_length = e1000_igp_cable_length_50;
+            break;
+        case e1000_cable_length_50_80:
+            *min_length = e1000_igp_cable_length_50;
+            *max_length = e1000_igp_cable_length_80;
+            break;
+        case e1000_cable_length_80_110:
+            *min_length = e1000_igp_cable_length_80;
+            *max_length = e1000_igp_cable_length_110;
+            break;
+        case e1000_cable_length_110_140:
+            *min_length = e1000_igp_cable_length_110;
+            *max_length = e1000_igp_cable_length_140;
+            break;
+        case e1000_cable_length_140:
+            *min_length = e1000_igp_cable_length_140;
+            *max_length = e1000_igp_cable_length_170;
+            break;
+        default:
+            return -E1000_ERR_PHY;
+            break;
+        }
+    } else if(hw->phy_type == e1000_phy_igp) { /* For IGP PHY */
+        uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+                                                         {IGP01E1000_PHY_AGC_A,
+                                                          IGP01E1000_PHY_AGC_B,
+                                                          IGP01E1000_PHY_AGC_C,
+                                                          IGP01E1000_PHY_AGC_D};
+        /* Read the AGC registers for all channels */
+        for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+
+            if((ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data)))
+                return ret_val;
+
+            cur_agc = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT;
+
+            /* Array bound check. */
+            if((cur_agc >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) ||
+               (cur_agc == 0))
+                return -E1000_ERR_PHY;
+
+            agc_value += cur_agc;
+
+            /* Update minimal AGC value. */
+            if(min_agc > cur_agc)
+                min_agc = cur_agc;
+        }
+
+        /* Remove the minimal AGC result for length < 50m */
+        if(agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) {
+            agc_value -= min_agc;
+
+            /* Get the average length of the remaining 3 channels */
+            agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1);
+        } else {
+            /* Get the average length of all the 4 channels. */
+            agc_value /= IGP01E1000_PHY_CHANNEL_NUM;
+        }
+
+        /* Set the range of the calculated length. */
+        *min_length = ((e1000_igp_cable_length_table[agc_value] -
+                       IGP01E1000_AGC_RANGE) > 0) ?
+                       (e1000_igp_cable_length_table[agc_value] -
+                       IGP01E1000_AGC_RANGE) : 0;
+        *max_length = e1000_igp_cable_length_table[agc_value] +
+                      IGP01E1000_AGC_RANGE;
+    }
+
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Check the cable polarity
+ *
+ * hw - Struct containing variables accessed by shared code
+ * polarity - output parameter : 0 - Polarity is not reversed
+ *                               1 - Polarity is reversed.
+ *
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS
+ *
+ * For phy's older then IGP, this function simply reads the polarity bit in the
+ * Phy Status register.  For IGP phy's, this bit is valid only if link speed is
+ * 10 Mbps.  If the link speed is 100 Mbps there is no polarity so this bit will
+ * return 0.  If the link speed is 1000 Mbps the polarity status is in the
+ * IGP01E1000_PHY_PCS_INIT_REG.
+ *****************************************************************************/
+int32_t
+e1000_check_polarity(struct e1000_hw *hw,
+                     uint16_t *polarity)
+{
+    int32_t ret_val;
+    uint16_t phy_data;
+
+    DEBUGFUNC("e1000_check_polarity");
+
+    if(hw->phy_type == e1000_phy_m88) {
+        /* return the Polarity bit in the Status register. */
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+        *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >>
+                    M88E1000_PSSR_REV_POLARITY_SHIFT;
+    } else if(hw->phy_type == e1000_phy_igp) {
+        /* Read the Status register to check the speed */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
+        /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to
+         * find the polarity status */
+        if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+           IGP01E1000_PSSR_SPEED_1000MBPS) {
+
+            /* Read the GIG initialization PCS register (0x00B4) */
+            if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG,
+                                             &phy_data)))
+                return ret_val;
+
+            /* Check the polarity bits */
+            *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? 1 : 0;
+        } else {
+            /* For 10 Mbps, read the polarity bit in the status register. (for
+             * 100 Mbps this bit is always 0) */
+            *polarity = phy_data & IGP01E1000_PSSR_POLARITY_REVERSED;
+        }
+    }
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Check if Downshift occured
+ *
+ * hw - Struct containing variables accessed by shared code
+ * downshift - output parameter : 0 - No Downshift ocured.
+ *                                1 - Downshift ocured.
+ *
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS 
+ *
+ * For phy's older then IGP, this function reads the Downshift bit in the Phy
+ * Specific Status register.  For IGP phy's, it reads the Downgrade bit in the
+ * Link Health register.  In IGP this bit is latched high, so the driver must
+ * read it immediately after link is established.
+ *****************************************************************************/
+int32_t
+e1000_check_downshift(struct e1000_hw *hw)
+{
+    int32_t ret_val;
+    uint16_t phy_data;
+
+    DEBUGFUNC("e1000_check_downshift");
+
+    if(hw->phy_type == e1000_phy_igp) {
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH,
+                                         &phy_data)))
+            return ret_val;
+
+        hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0;
+    }
+    else if(hw->phy_type == e1000_phy_m88) {
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
+        hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >>
+                               M88E1000_PSSR_DOWNSHIFT_SHIFT;
+    }
+    return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a
+ * gigabit link is achieved to improve link quality.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ *            E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+int32_t
+e1000_config_dsp_after_link_change(struct e1000_hw *hw,
+                                   boolean_t link_up)
+{
+    int32_t ret_val;
+    uint16_t phy_data, speed, duplex, i;
+    uint16_t dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+                                        {IGP01E1000_PHY_AGC_PARAM_A,
+                                        IGP01E1000_PHY_AGC_PARAM_B,
+                                        IGP01E1000_PHY_AGC_PARAM_C,
+                                        IGP01E1000_PHY_AGC_PARAM_D};
+    uint16_t min_length, max_length;
+
+    DEBUGFUNC("e1000_config_dsp_after_link_change");
+
+    if(hw->phy_type != e1000_phy_igp)
+        return E1000_SUCCESS;
+
+    if(link_up) {
+        if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) {
+            DEBUGOUT("Error getting link speed and duplex\n");
+            return ret_val;
+        }
+
+        if(speed == SPEED_1000) {
+
+            e1000_get_cable_length(hw, &min_length, &max_length);
+
+            if((hw->dsp_config_state == e1000_dsp_config_enabled) &&
+                min_length >= e1000_igp_cable_length_50) {
+
+                for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                    if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i],
+                                                     &phy_data)))
+                        return ret_val;
+
+                    phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+
+                    if((ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i],
+                                                      phy_data)))
+                        return ret_val;
+                }
+                hw->dsp_config_state = e1000_dsp_config_activated;
+            }
+
+            if((hw->ffe_config_state == e1000_ffe_config_enabled) &&
+               (min_length < e1000_igp_cable_length_50)) {
+
+                uint16_t ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20;
+                uint32_t idle_errs = 0;
+
+                /* clear previous idle error counts */
+                if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+                                                 &phy_data)))
+                    return ret_val;
+
+                for(i = 0; i < ffe_idle_err_timeout; i++) {
+                    usec_delay(1000);
+                    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+                                                     &phy_data)))
+                        return ret_val;
+
+                    idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT);
+                    if(idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) {
+                        hw->ffe_config_state = e1000_ffe_config_active;
+
+                        if((ret_val = e1000_write_phy_reg(hw,
+                                    IGP01E1000_PHY_DSP_FFE,
+                                    IGP01E1000_PHY_DSP_FFE_CM_CP)))
+                            return ret_val;
+                        break;
+                    }
+
+                    if(idle_errs)
+                        ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100;
+                }
+            }
+        }
+    } else {
+        if(hw->dsp_config_state == e1000_dsp_config_activated) {
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                IGP01E1000_IEEE_FORCE_GIGA)))
+                return ret_val;
+            for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i],
+                                                 &phy_data)))
+                    return ret_val;
+
+                phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+                phy_data |=  IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS;
+
+                if((ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i],
+                                                  phy_data)))
+                    return ret_val;
+            }
+
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_RESTART_AUTONEG)))
+                return ret_val;
+
+            hw->dsp_config_state = e1000_dsp_config_enabled;
+        }
+
+        if(hw->ffe_config_state == e1000_ffe_config_active) {
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_FORCE_GIGA)))
+                return ret_val;
+            if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE,
+                                              IGP01E1000_PHY_DSP_FFE_DEFAULT)))
+                return ret_val;
+
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_RESTART_AUTONEG)))
+                return ret_val;
+        hw->ffe_config_state = e1000_ffe_config_enabled;
+        }
+    }
+    return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Workaround for the 82547 long TTL on noisy 100HD hubs.
+ *
+ * This function, specific to 82547 hardware only, needs to be called every
+ * second.  It checks if a parallel detect fault has occurred.  If a fault
+ * occurred, disable/enable the DSP reset mechanism up to 5 times (once per
+ * second).  If link is established, stop the workaround and ensure the DSP
+ * reset is enabled.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ *            E1000_SUCCESS in any other case
+ *
+ ****************************************************************************/
+int32_t
+e1000_igp_ttl_workaround(struct e1000_hw *hw)
+{
+    int32_t ret_val;
+    uint16_t phy_data = 0;
+    uint16_t dsp_value = DSP_RESET_ENABLE;
+
+    if(((hw->mac_type != e1000_82541) && (hw->mac_type != e1000_82547)) ||
+       (!hw->ttl_wa_activation)) {
+        return E1000_SUCCESS;
+    }
+
+    /* Check for link first */
+    if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+        return ret_val;
+
+    if(phy_data & MII_SR_LINK_STATUS) {
+        /* If link is established during the workaround, the DSP mechanism must
+         * be enabled. */
+        if(hw->dsp_reset_counter) {
+            hw->dsp_reset_counter = 0;
+            dsp_value = DSP_RESET_ENABLE;
+        } else
+            return E1000_SUCCESS;
+    } else {
+        if(hw->dsp_reset_counter == 0) {
+            /* Workaround not activated, check if it needs activation */
+            if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data)))
+                return ret_val;
+            /* Activate the workaround if there was a parallel detect fault */
+            if(phy_data & NWAY_ER_PAR_DETECT_FAULT)
+                hw->dsp_reset_counter++;
+            else
+                return E1000_SUCCESS;
+        }
+
+        if(hw->dsp_reset_counter) {
+            /* After 5 times, stop the workaround */
+            if(hw->dsp_reset_counter > E1000_MAX_DSP_RESETS) {
+                hw->dsp_reset_counter = 0;
+                dsp_value = DSP_RESET_ENABLE;
+            } else {
+                dsp_value = (hw->dsp_reset_counter & 1) ? DSP_RESET_DISABLE :
+                            DSP_RESET_ENABLE;
+                hw->dsp_reset_counter++;
+            }
+        }
+    }
+
+    if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_RESET, dsp_value)))
+        return ret_val;
+
+    return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * This function sets the lplu state according to the active flag.  When
+ * activating lplu this function also disables smart speed and vise versa.
+ * lplu will not be activated unless the device autonegotiation advertisment
+ * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes.
+ * hw: Struct containing variables accessed by shared code
+ * active - true to enable lplu false to disable lplu.
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ *            E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+int32_t
+e1000_set_d3_lplu_state(struct e1000_hw *hw,
+                        boolean_t active)
+{
+    int32_t ret_val;
+    uint16_t phy_data;
+    DEBUGFUNC("e1000_set_d3_lplu_state");
+
+    if(!((hw->mac_type == e1000_82541_rev_2) ||
+         (hw->mac_type == e1000_82547_rev_2)))
+        return E1000_SUCCESS;
+
+    /* During driver activity LPLU should not be used or it will attain link
+     * from the lowest speeds starting from 10Mbps. The capability is used for
+     * Dx transitions and states */
+    if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data)))
+        return ret_val;
+
+    if(!active) {
+        phy_data &= ~IGP01E1000_GMII_FLEX_SPD;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data)))
+            return ret_val;
+
+        /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used during
+         * Dx states where the power conservation is most important.  During
+         * driver activity we should enable SmartSpeed, so performance is
+         * maintained. */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data |= IGP01E1000_PSCFR_SMART_SPEED;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                          phy_data)))
+            return ret_val;
+
+    } else if((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) ||
+              (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) ||
+              (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) {
+
+        phy_data |= IGP01E1000_GMII_FLEX_SPD;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data)))
+            return ret_val;
+
+        /* When LPLU is enabled we should disable SmartSpeed */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                          phy_data)))
+            return ret_val;
+
+    }
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Change VCO speed register to improve Bit Error Rate performance of SERDES.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_set_vco_speed(struct e1000_hw *hw)
+{
+    int32_t  ret_val;
+    uint16_t default_page = 0;
+    uint16_t phy_data;
+
+    DEBUGFUNC("e1000_set_vco_speed");
+
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+       break;
+    default:
+        return E1000_SUCCESS;
+    }
+
+    /* Set PHY register 30, page 5, bit 8 to 0 */
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                     &default_page)))
+        return ret_val;
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data)))
+        return ret_val;
+
+    phy_data &= ~M88E1000_PHY_VCO_REG_BIT8;
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data)))
+        return ret_val;
+
+    /* Set PHY register 30, page 4, bit 11 to 1 */
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data)))
+        return ret_val;
+
+    phy_data |= M88E1000_PHY_VCO_REG_BIT11;
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data)))
+        return ret_val;
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                      default_page)))
+        return ret_val;
+
+    return E1000_SUCCESS;
+}
+
index 812dfd140f3579ae3f86bf7cd17b993f7be0b7f0..4976a3c0843234360525ec43d109c7fdf59fd6d7 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -35,6 +35,7 @@
 
 #include "e1000_osdep.h"
 
+
 /* Forward declarations of structures used by the shared code */
 struct e1000_hw;
 struct e1000_hw_stats;
@@ -49,14 +50,28 @@ typedef enum {
     e1000_82544,
     e1000_82540,
     e1000_82545,
+    e1000_82545_rev_3,
     e1000_82546,
+    e1000_82546_rev_3,
+    e1000_82541,
+    e1000_82541_rev_2,
+    e1000_82547,
+    e1000_82547_rev_2,
     e1000_num_macs
 } e1000_mac_type;
 
+typedef enum {
+    e1000_eeprom_uninitialized = 0,
+    e1000_eeprom_spi,
+    e1000_eeprom_microwire,
+    e1000_num_eeprom_types
+} e1000_eeprom_type;
+
 /* Media Types */
 typedef enum {
     e1000_media_type_copper = 0,
     e1000_media_type_fiber = 1,
+    e1000_media_type_internal_serdes = 2,
     e1000_num_media_types
 } e1000_media_type;
 
@@ -80,7 +95,8 @@ typedef enum {
 typedef enum {
     e1000_bus_type_unknown = 0,
     e1000_bus_type_pci,
-    e1000_bus_type_pcix
+    e1000_bus_type_pcix,
+    e1000_bus_type_reserved
 } e1000_bus_type;
 
 /* PCI bus speeds */
@@ -89,6 +105,7 @@ typedef enum {
     e1000_bus_speed_33,
     e1000_bus_speed_66,
     e1000_bus_speed_100,
+    e1000_bus_speed_120,
     e1000_bus_speed_133,
     e1000_bus_speed_reserved
 } e1000_bus_speed;
@@ -97,7 +114,8 @@ typedef enum {
 typedef enum {
     e1000_bus_width_unknown = 0,
     e1000_bus_width_32,
-    e1000_bus_width_64
+    e1000_bus_width_64,
+    e1000_bus_width_reserved
 } e1000_bus_width;
 
 /* PHY status info structure and supporting enums */
@@ -110,6 +128,27 @@ typedef enum {
     e1000_cable_length_undefined = 0xFF
 } e1000_cable_length;
 
+typedef enum {
+    e1000_igp_cable_length_10  = 10,
+    e1000_igp_cable_length_20  = 20,
+    e1000_igp_cable_length_30  = 30,
+    e1000_igp_cable_length_40  = 40,
+    e1000_igp_cable_length_50  = 50,
+    e1000_igp_cable_length_60  = 60,
+    e1000_igp_cable_length_70  = 70,
+    e1000_igp_cable_length_80  = 80,
+    e1000_igp_cable_length_90  = 90,
+    e1000_igp_cable_length_100 = 100,
+    e1000_igp_cable_length_110 = 110,
+    e1000_igp_cable_length_120 = 120,
+    e1000_igp_cable_length_130 = 130,
+    e1000_igp_cable_length_140 = 140,
+    e1000_igp_cable_length_150 = 150,
+    e1000_igp_cable_length_160 = 160,
+    e1000_igp_cable_length_170 = 170,
+    e1000_igp_cable_length_180 = 180
+} e1000_igp_cable_length;
+
 typedef enum {
     e1000_10bt_ext_dist_enable_normal = 0,
     e1000_10bt_ext_dist_enable_lower,
@@ -122,6 +161,12 @@ typedef enum {
     e1000_rev_polarity_undefined = 0xFF
 } e1000_rev_polarity;
 
+typedef enum {
+    e1000_downshift_normal = 0,
+    e1000_downshift_activated,
+    e1000_downshift_undefined = 0xFF
+} e1000_downshift;
+
 typedef enum {
     e1000_polarity_reversal_enabled = 0,
     e1000_polarity_reversal_disabled,
@@ -142,10 +187,37 @@ typedef enum {
     e1000_1000t_rx_status_undefined = 0xFF
 } e1000_1000t_rx_status;
 
+typedef enum {
+    e1000_phy_m88 = 0,
+    e1000_phy_igp,
+    e1000_phy_undefined = 0xFF
+} e1000_phy_type;
+
+typedef enum {
+    e1000_ms_hw_default = 0,
+    e1000_ms_force_master,
+    e1000_ms_force_slave,
+    e1000_ms_auto
+} e1000_ms_type;
+
+typedef enum {
+    e1000_ffe_config_enabled = 0,
+    e1000_ffe_config_active,
+    e1000_ffe_config_blocked
+} e1000_ffe_config;
+
+typedef enum {
+    e1000_dsp_config_disabled = 0,
+    e1000_dsp_config_enabled,
+    e1000_dsp_config_activated,
+    e1000_dsp_config_undefined = 0xFF
+} e1000_dsp_config;
+
 struct e1000_phy_info {
     e1000_cable_length cable_length;
     e1000_10bt_ext_dist_enable extended_10bt_distance;
     e1000_rev_polarity cable_polarity;
+    e1000_downshift downshift;
     e1000_polarity_reversal polarity_correction;
     e1000_auto_x_mode mdix_mode;
     e1000_1000t_rx_status local_rx;
@@ -157,6 +229,15 @@ struct e1000_phy_stats {
     uint32_t receive_errors;
 };
 
+struct e1000_eeprom_info {
+    e1000_eeprom_type type;
+    uint16_t word_size;
+    uint16_t opcode_bits;
+    uint16_t address_bits;
+    uint16_t delay_usec;
+    uint16_t page_size;
+};
+
 
 
 /* Error Codes */
@@ -166,12 +247,14 @@ struct e1000_phy_stats {
 #define E1000_ERR_CONFIG   3
 #define E1000_ERR_PARAM    4
 #define E1000_ERR_MAC_TYPE 5
+#define E1000_ERR_PHY_TYPE 6
 
 /* Function prototypes */
 /* Initialization */
-void e1000_reset_hw(struct e1000_hw *hw);
+int32_t e1000_reset_hw(struct e1000_hw *hw);
 int32_t e1000_init_hw(struct e1000_hw *hw);
 int32_t e1000_set_mac_type(struct e1000_hw *hw);
+void e1000_set_media_type(struct e1000_hw *hw);
 
 /* Link Configuration */
 int32_t e1000_setup_link(struct e1000_hw *hw);
@@ -179,8 +262,9 @@ int32_t e1000_phy_setup_autoneg(struct e1000_hw *hw);
 void e1000_config_collision_dist(struct e1000_hw *hw);
 int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
 int32_t e1000_check_for_link(struct e1000_hw *hw);
-void e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
+int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
 int32_t e1000_wait_autoneg(struct e1000_hw *hw);
+int32_t e1000_force_mac_fc(struct e1000_hw *hw);
 
 /* PHY */
 int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
@@ -189,13 +273,19 @@ void e1000_phy_hw_reset(struct e1000_hw *hw);
 int32_t e1000_phy_reset(struct e1000_hw *hw);
 int32_t e1000_detect_gig_phy(struct e1000_hw *hw);
 int32_t e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+int32_t e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+int32_t e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+int32_t e1000_get_cable_length(struct e1000_hw *hw, uint16_t *min_length, uint16_t *max_length);
+int32_t e1000_check_polarity(struct e1000_hw *hw, uint16_t *polarity);
+int32_t e1000_check_downshift(struct e1000_hw *hw);
 int32_t e1000_validate_mdi_setting(struct e1000_hw *hw);
 
 /* EEPROM Functions */
-int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t *data);
+void e1000_init_eeprom_params(struct e1000_hw *hw);
+int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data);
 int32_t e1000_validate_eeprom_checksum(struct e1000_hw *hw);
 int32_t e1000_update_eeprom_checksum(struct e1000_hw *hw);
-int32_t e1000_write_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t data);
+int32_t e1000_write_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data);
 int32_t e1000_read_part_num(struct e1000_hw *hw, uint32_t * part_num);
 int32_t e1000_read_mac_addr(struct e1000_hw * hw);
 
@@ -231,6 +321,10 @@ uint32_t e1000_io_read(struct e1000_hw *hw, uint32_t port);
 uint32_t e1000_read_reg_io(struct e1000_hw *hw, uint32_t offset);
 void e1000_io_write(struct e1000_hw *hw, uint32_t port, uint32_t value);
 void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
+int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, boolean_t link_up);
+int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active);
+int32_t e1000_igp_ttl_workaround(struct e1000_hw *hw);
+
 #define E1000_READ_REG_IO(a, reg) \
     e1000_read_reg_io((a), E1000_##reg)
 #define E1000_WRITE_REG_IO(a, reg, val) \
@@ -251,9 +345,22 @@ void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
 #define E1000_DEV_ID_82540EP_LP          0x101E
 #define E1000_DEV_ID_82545EM_COPPER      0x100F
 #define E1000_DEV_ID_82545EM_FIBER       0x1011
+#define E1000_DEV_ID_82545GM_COPPER      0x1026
+#define E1000_DEV_ID_82545GM_FIBER       0x1027
+#define E1000_DEV_ID_82545GM_SERDES      0x1028
 #define E1000_DEV_ID_82546EB_COPPER      0x1010
 #define E1000_DEV_ID_82546EB_FIBER       0x1012
-#define NUM_DEV_IDS 16
+#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
+#define E1000_DEV_ID_82541EI             0x1013
+#define E1000_DEV_ID_82541EI_MOBILE      0x1018
+#define E1000_DEV_ID_82541ER             0x1078
+#define E1000_DEV_ID_82547GI             0x1075
+#define E1000_DEV_ID_82541GI             0x1076
+#define E1000_DEV_ID_82541GI_MOBILE      0x1077
+#define E1000_DEV_ID_82546GB_COPPER      0x1079
+#define E1000_DEV_ID_82546GB_FIBER       0x107A
+#define E1000_DEV_ID_82546GB_SERDES      0x107B
+#define E1000_DEV_ID_82547EI             0x1019
 
 #define NODE_ADDRESS_SIZE 6
 #define ETH_LENGTH_OF_ADDRESS 6
@@ -298,7 +405,7 @@ void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
 /* This defines the bits that are set in the Interrupt Mask
  * Set/Read Register.  Each bit is documented below:
  *   o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
- *   o RXSEQ  = Receive Sequence Error 
+ *   o RXSEQ  = Receive Sequence Error
  */
 #define POLL_IMS_ENABLE_MASK ( \
     E1000_IMS_RXDMT0 |         \
@@ -319,12 +426,12 @@ void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
     E1000_IMS_RXSEQ  |    \
     E1000_IMS_LSC)
 
-/* The number of high/low register pairs in the RAR. The RAR (Receive Address
+/* Number of high/low register pairs in the RAR. The RAR (Receive Address
  * Registers) holds the directed and multicast addresses that we monitor. We
  * reserve one of these spots for our directed address, allowing us room for
- * E1000_RAR_ENTRIES - 1 multicast addresses. 
+ * E1000_RAR_ENTRIES - 1 multicast addresses.
  */
-#define E1000_RAR_ENTRIES 16
+#define E1000_RAR_ENTRIES 15
 
 #define MIN_NUMBER_OF_DESCRIPTORS 8
 #define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8
@@ -473,7 +580,7 @@ struct e1000_rar {
     volatile uint32_t high;     /* receive address high */
 };
 
-/* The number of entries in the Multicast Table Array (MTA). */
+/* Number of entries in the Multicast Table Array (MTA). */
 #define E1000_NUM_MTA_REGISTERS 128
 
 /* IPv4 Address Table Entry */
@@ -523,7 +630,7 @@ struct e1000_ffvt_entry {
 /* Register Set. (82543, 82544)
  *
  * Registers are defined to be 32 bits and  should be accessed as 32 bit values.
- * These registers are physically located on the NIC, but are mapped into the 
+ * These registers are physically located on the NIC, but are mapped into the
  * host memory address space.
  *
  * RW - register is both readable and writable
@@ -533,10 +640,12 @@ struct e1000_ffvt_entry {
  * A - register array
  */
 #define E1000_CTRL     0x00000  /* Device Control - RW */
+#define E1000_CTRL_DUP 0x00004  /* Device Control Duplicate (Shadow) - RW */
 #define E1000_STATUS   0x00008  /* Device Status - RO */
 #define E1000_EECD     0x00010  /* EEPROM/Flash Control - RW */
 #define E1000_EERD     0x00014  /* EEPROM Read - RW */
 #define E1000_CTRL_EXT 0x00018  /* Extended Device Control - RW */
+#define E1000_FLA      0x0001C  /* Flash Access - RW */
 #define E1000_MDIC     0x00020  /* MDI Control - RW */
 #define E1000_FCAL     0x00028  /* Flow Control Address Low - RW */
 #define E1000_FCAH     0x0002C  /* Flow Control Address High -RW */
@@ -569,6 +678,11 @@ struct e1000_ffvt_entry {
 #define E1000_RADV     0x0282C  /* RX Interrupt Absolute Delay Timer - RW */
 #define E1000_RSRPD    0x02C00  /* RX Small Packet Detect - RW */
 #define E1000_TXDMAC   0x03000  /* TX DMA Control - RW */
+#define E1000_TDFH     0x03410  /* TX Data FIFO Head - RW */
+#define E1000_TDFT     0x03418  /* TX Data FIFO Tail - RW */
+#define E1000_TDFHS    0x03420  /* TX Data FIFO Head Saved - RW */
+#define E1000_TDFTS    0x03428  /* TX Data FIFO Tail Saved - RW */
+#define E1000_TDFPC    0x03430  /* TX Data FIFO Packet Count - RW */
 #define E1000_TDBAL    0x03800  /* TX Descriptor Base Address Low - RW */
 #define E1000_TDBAH    0x03804  /* TX Descriptor Base Address High - RW */
 #define E1000_TDLEN    0x03808  /* TX Descriptor Length - RW */
@@ -660,10 +774,12 @@ struct e1000_ffvt_entry {
  * the registers function in the same manner.
  */
 #define E1000_82542_CTRL     E1000_CTRL
+#define E1000_82542_CTRL_DUP E1000_CTRL_DUP
 #define E1000_82542_STATUS   E1000_STATUS
 #define E1000_82542_EECD     E1000_EECD
 #define E1000_82542_EERD     E1000_EERD
 #define E1000_82542_CTRL_EXT E1000_CTRL_EXT
+#define E1000_82542_FLA      E1000_FLA
 #define E1000_82542_MDIC     E1000_MDIC
 #define E1000_82542_FCAL     E1000_FCAL
 #define E1000_82542_FCAH     E1000_FCAH
@@ -705,6 +821,9 @@ struct e1000_ffvt_entry {
 #define E1000_82542_RADV     E1000_RADV
 #define E1000_82542_RSRPD    E1000_RSRPD
 #define E1000_82542_TXDMAC   E1000_TXDMAC
+#define E1000_82542_TDFHS    E1000_TDFHS
+#define E1000_82542_TDFTS    E1000_TDFTS
+#define E1000_82542_TDFPC    E1000_TDFPC
 #define E1000_82542_TXDCTL   E1000_TXDCTL
 #define E1000_82542_TADV     E1000_TADV
 #define E1000_82542_TSPMT    E1000_TSPMT
@@ -777,6 +896,8 @@ struct e1000_ffvt_entry {
 #define E1000_82542_WUPL     E1000_WUPL
 #define E1000_82542_WUPM     E1000_WUPM
 #define E1000_82542_FFLT     E1000_FFLT
+#define E1000_82542_TDFH     0x08010
+#define E1000_82542_TDFT     0x08018
 #define E1000_82542_FFMT     E1000_FFMT
 #define E1000_82542_FFVT     E1000_FFVT
 
@@ -846,12 +967,18 @@ struct e1000_hw_stats {
 struct e1000_hw {
     uint8_t *hw_addr;
     e1000_mac_type mac_type;
+    e1000_phy_type phy_type;
+    uint32_t phy_init_script;
     e1000_media_type media_type;
     void *back;
     e1000_fc_type fc;
     e1000_bus_speed bus_speed;
     e1000_bus_width bus_width;
     e1000_bus_type bus_type;
+    struct e1000_eeprom_info eeprom;
+    e1000_ms_type master_slave;
+    e1000_ms_type original_master_slave;
+    e1000_ffe_config ffe_config_state;
     uint32_t io_base;
     uint32_t phy_id;
     uint32_t phy_revision;
@@ -868,6 +995,8 @@ struct e1000_hw {
     uint32_t ledctl_default;
     uint32_t ledctl_mode1;
     uint32_t ledctl_mode2;
+    uint16_t phy_spd_default;
+    uint16_t dsp_reset_counter;
     uint16_t autoneg_advertised;
     uint16_t pci_cmd_word;
     uint16_t fc_high_water;
@@ -891,10 +1020,15 @@ struct e1000_hw {
     uint8_t mac_addr[NODE_ADDRESS_SIZE];
     uint8_t perm_mac_addr[NODE_ADDRESS_SIZE];
     boolean_t disable_polarity_correction;
+    boolean_t speed_downgraded;
+    boolean_t ttl_wa_activation;
+    e1000_dsp_config dsp_config_state;
     boolean_t get_link_status;
     boolean_t tbi_compatibility_en;
     boolean_t tbi_compatibility_on;
+    boolean_t phy_reset_disable;
     boolean_t fc_send_xon;
+    boolean_t fc_strict_ieee;
     boolean_t report_tx_early;
     boolean_t adaptive_ifs;
     boolean_t ifs_params_forced;
@@ -967,14 +1101,20 @@ struct e1000_hw {
 #define E1000_EECD_CS        0x00000002 /* EEPROM Chip Select */
 #define E1000_EECD_DI        0x00000004 /* EEPROM Data In */
 #define E1000_EECD_DO        0x00000008 /* EEPROM Data Out */
-#define E1000_EECD_FWE_MASK  0x00000030 
+#define E1000_EECD_FWE_MASK  0x00000030
 #define E1000_EECD_FWE_DIS   0x00000010 /* Disable FLASH writes */
 #define E1000_EECD_FWE_EN    0x00000020 /* Enable FLASH writes */
 #define E1000_EECD_FWE_SHIFT 4
-#define E1000_EECD_SIZE      0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
 #define E1000_EECD_REQ       0x00000040 /* EEPROM Access Request */
 #define E1000_EECD_GNT       0x00000080 /* EEPROM Access Grant */
 #define E1000_EECD_PRES      0x00000100 /* EEPROM Present */
+#define E1000_EECD_SIZE      0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
+#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type
+                                         * (0-small, 1-large) */
+#define E1000_EECD_TYPE      0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */
+#ifndef E1000_EEPROM_GRANT_ATTEMPTS
+#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
 
 /* EEPROM Read */
 #define E1000_EERD_START      0x00000001 /* Start Read */
@@ -984,8 +1124,15 @@ struct e1000_hw {
 #define E1000_EERD_DATA_SHIFT 16
 #define E1000_EERD_DATA_MASK  0xFFFF0000 /* Read Data */
 
+/* SPI EEPROM Status Register */
+#define EEPROM_STATUS_RDY_SPI  0x01
+#define EEPROM_STATUS_WEN_SPI  0x02
+#define EEPROM_STATUS_BP0_SPI  0x04
+#define EEPROM_STATUS_BP1_SPI  0x08
+#define EEPROM_STATUS_WPEN_SPI 0x80
+
 /* Extended Device Control */
-#define E1000_CTRL_EXT_GPI0_EN   0x00000001 /* Maps SDP4 to GPI0 */ 
+#define E1000_CTRL_EXT_GPI0_EN   0x00000001 /* Maps SDP4 to GPI0 */
 #define E1000_CTRL_EXT_GPI1_EN   0x00000002 /* Maps SDP5 to GPI1 */
 #define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN
 #define E1000_CTRL_EXT_GPI2_EN   0x00000004 /* Maps SDP6 to GPI2 */
@@ -1025,22 +1172,22 @@ struct e1000_hw {
 #define E1000_MDIC_ERROR     0x40000000
 
 /* LED Control */
-#define E1000_LEDCTL_LED0_MODE_MASK  0x0000000F
-#define E1000_LEDCTL_LED0_MODE_SHIFT 0
-#define E1000_LEDCTL_LED0_IVRT       0x00000040
-#define E1000_LEDCTL_LED0_BLINK      0x00000080
-#define E1000_LEDCTL_LED1_MODE_MASK  0x00000F00
-#define E1000_LEDCTL_LED1_MODE_SHIFT 8
-#define E1000_LEDCTL_LED1_IVRT       0x00004000
-#define E1000_LEDCTL_LED1_BLINK      0x00008000
-#define E1000_LEDCTL_LED2_MODE_MASK  0x000F0000
-#define E1000_LEDCTL_LED2_MODE_SHIFT 16
-#define E1000_LEDCTL_LED2_IVRT       0x00400000
-#define E1000_LEDCTL_LED2_BLINK      0x00800000
-#define E1000_LEDCTL_LED3_MODE_MASK  0x0F000000
-#define E1000_LEDCTL_LED3_MODE_SHIFT 24
-#define E1000_LEDCTL_LED3_IVRT       0x40000000
-#define E1000_LEDCTL_LED3_BLINK      0x80000000
+#define E1000_LEDCTL_LED0_MODE_MASK       0x0000000F
+#define E1000_LEDCTL_LED0_MODE_SHIFT      0
+#define E1000_LEDCTL_LED0_IVRT            0x00000040
+#define E1000_LEDCTL_LED0_BLINK           0x00000080
+#define E1000_LEDCTL_LED1_MODE_MASK       0x00000F00
+#define E1000_LEDCTL_LED1_MODE_SHIFT      8
+#define E1000_LEDCTL_LED1_IVRT            0x00004000
+#define E1000_LEDCTL_LED1_BLINK           0x00008000
+#define E1000_LEDCTL_LED2_MODE_MASK       0x000F0000
+#define E1000_LEDCTL_LED2_MODE_SHIFT      16
+#define E1000_LEDCTL_LED2_IVRT            0x00400000
+#define E1000_LEDCTL_LED2_BLINK           0x00800000
+#define E1000_LEDCTL_LED3_MODE_MASK       0x0F000000
+#define E1000_LEDCTL_LED3_MODE_SHIFT      24
+#define E1000_LEDCTL_LED3_IVRT            0x40000000
+#define E1000_LEDCTL_LED3_BLINK           0x80000000
 
 #define E1000_LEDCTL_MODE_LINK_10_1000  0x0
 #define E1000_LEDCTL_MODE_LINK_100_1000 0x1
@@ -1063,109 +1210,109 @@ struct e1000_hw {
 #define E1000_RAH_AV  0x80000000        /* Receive descriptor valid */
 
 /* Interrupt Cause Read */
-#define E1000_ICR_TXDW    0x00000001    /* Transmit desc written back */
-#define E1000_ICR_TXQE    0x00000002    /* Transmit Queue empty */
-#define E1000_ICR_LSC     0x00000004    /* Link Status Change */
-#define E1000_ICR_RXSEQ   0x00000008    /* rx sequence error */
-#define E1000_ICR_RXDMT0  0x00000010    /* rx desc min. threshold (0) */
-#define E1000_ICR_RXO     0x00000040    /* rx overrun */
-#define E1000_ICR_RXT0    0x00000080    /* rx timer intr (ring 0) */
-#define E1000_ICR_MDAC    0x00000200    /* MDIO access complete */
-#define E1000_ICR_RXCFG   0x00000400    /* RX /c/ ordered set */
-#define E1000_ICR_GPI_EN0 0x00000800    /* GP Int 0 */
-#define E1000_ICR_GPI_EN1 0x00001000    /* GP Int 1 */
-#define E1000_ICR_GPI_EN2 0x00002000    /* GP Int 2 */
-#define E1000_ICR_GPI_EN3 0x00004000    /* GP Int 3 */
-#define E1000_ICR_TXD_LOW 0x00008000
-#define E1000_ICR_SRPD    0x00010000
+#define E1000_ICR_TXDW          0x00000001 /* Transmit desc written back */
+#define E1000_ICR_TXQE          0x00000002 /* Transmit Queue empty */
+#define E1000_ICR_LSC           0x00000004 /* Link Status Change */
+#define E1000_ICR_RXSEQ         0x00000008 /* rx sequence error */
+#define E1000_ICR_RXDMT0        0x00000010 /* rx desc min. threshold (0) */
+#define E1000_ICR_RXO           0x00000040 /* rx overrun */
+#define E1000_ICR_RXT0          0x00000080 /* rx timer intr (ring 0) */
+#define E1000_ICR_MDAC          0x00000200 /* MDIO access complete */
+#define E1000_ICR_RXCFG         0x00000400 /* RX /c/ ordered set */
+#define E1000_ICR_GPI_EN0       0x00000800 /* GP Int 0 */
+#define E1000_ICR_GPI_EN1       0x00001000 /* GP Int 1 */
+#define E1000_ICR_GPI_EN2       0x00002000 /* GP Int 2 */
+#define E1000_ICR_GPI_EN3       0x00004000 /* GP Int 3 */
+#define E1000_ICR_TXD_LOW       0x00008000
+#define E1000_ICR_SRPD          0x00010000
 
 /* Interrupt Cause Set */
-#define E1000_ICS_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_ICS_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_ICS_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_ICS_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_ICS_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_ICS_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_ICS_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_ICS_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_ICS_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_ICS_SRPD    E1000_ICR_SRPD
+#define E1000_ICS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_ICS_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_ICS_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_ICS_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_ICS_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_ICS_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_ICS_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_ICS_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_ICS_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_ICS_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_ICS_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_ICS_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_ICS_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_ICS_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_ICS_SRPD      E1000_ICR_SRPD
 
 /* Interrupt Mask Set */
-#define E1000_IMS_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_IMS_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_IMS_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_IMS_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_IMS_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_IMS_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_IMS_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_IMS_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_IMS_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_IMS_SRPD    E1000_ICR_SRPD
+#define E1000_IMS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_IMS_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_IMS_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_IMS_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_IMS_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_IMS_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_IMS_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_IMS_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_IMS_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_IMS_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_IMS_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_IMS_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_IMS_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_IMS_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_IMS_SRPD      E1000_ICR_SRPD
 
 /* Interrupt Mask Clear */
-#define E1000_IMC_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_IMC_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_IMC_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_IMC_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_IMC_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_IMC_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_IMC_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_IMC_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_IMC_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_IMC_SRPD    E1000_ICR_SRPD
+#define E1000_IMC_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_IMC_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_IMC_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_IMC_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_IMC_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_IMC_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_IMC_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_IMC_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_IMC_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_IMC_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_IMC_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_IMC_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_IMC_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_IMC_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_IMC_SRPD      E1000_ICR_SRPD
 
 /* Receive Control */
-#define E1000_RCTL_RST          0x00000001      /* Software reset */
-#define E1000_RCTL_EN           0x00000002      /* enable */
-#define E1000_RCTL_SBP          0x00000004      /* store bad packet */
-#define E1000_RCTL_UPE          0x00000008      /* unicast promiscuous enable */
-#define E1000_RCTL_MPE          0x00000010      /* multicast promiscuous enab */
-#define E1000_RCTL_LPE          0x00000020      /* long packet enable */
-#define E1000_RCTL_LBM_NO       0x00000000      /* no loopback mode */
-#define E1000_RCTL_LBM_MAC      0x00000040      /* MAC loopback mode */
-#define E1000_RCTL_LBM_SLP      0x00000080      /* serial link loopback mode */
-#define E1000_RCTL_LBM_TCVR     0x000000C0      /* tcvr loopback mode */
-#define E1000_RCTL_RDMTS_HALF   0x00000000      /* rx desc min threshold size */
-#define E1000_RCTL_RDMTS_QUAT   0x00000100      /* rx desc min threshold size */
-#define E1000_RCTL_RDMTS_EIGTH  0x00000200      /* rx desc min threshold size */
-#define E1000_RCTL_MO_SHIFT     12              /* multicast offset shift */
-#define E1000_RCTL_MO_0         0x00000000      /* multicast offset 11:0 */
-#define E1000_RCTL_MO_1         0x00001000      /* multicast offset 12:1 */
-#define E1000_RCTL_MO_2         0x00002000      /* multicast offset 13:2 */
-#define E1000_RCTL_MO_3         0x00003000      /* multicast offset 15:4 */
-#define E1000_RCTL_MDR          0x00004000      /* multicast desc ring 0 */
-#define E1000_RCTL_BAM          0x00008000      /* broadcast enable */
+#define E1000_RCTL_RST            0x00000001    /* Software reset */
+#define E1000_RCTL_EN             0x00000002    /* enable */
+#define E1000_RCTL_SBP            0x00000004    /* store bad packet */
+#define E1000_RCTL_UPE            0x00000008    /* unicast promiscuous enable */
+#define E1000_RCTL_MPE            0x00000010    /* multicast promiscuous enab */
+#define E1000_RCTL_LPE            0x00000020    /* long packet enable */
+#define E1000_RCTL_LBM_NO         0x00000000    /* no loopback mode */
+#define E1000_RCTL_LBM_MAC        0x00000040    /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP        0x00000080    /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR       0x000000C0    /* tcvr loopback mode */
+#define E1000_RCTL_RDMTS_HALF     0x00000000    /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT     0x00000100    /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH    0x00000200    /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT       12            /* multicast offset shift */
+#define E1000_RCTL_MO_0           0x00000000    /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1           0x00001000    /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2           0x00002000    /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3           0x00003000    /* multicast offset 15:4 */
+#define E1000_RCTL_MDR            0x00004000    /* multicast desc ring 0 */
+#define E1000_RCTL_BAM            0x00008000    /* broadcast enable */
 /* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
-#define E1000_RCTL_SZ_2048      0x00000000      /* rx buffer size 2048 */
-#define E1000_RCTL_SZ_1024      0x00010000      /* rx buffer size 1024 */
-#define E1000_RCTL_SZ_512       0x00020000      /* rx buffer size 512 */
-#define E1000_RCTL_SZ_256       0x00030000      /* rx buffer size 256 */
+#define E1000_RCTL_SZ_2048        0x00000000    /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024        0x00010000    /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512         0x00020000    /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256         0x00030000    /* rx buffer size 256 */
 /* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
-#define E1000_RCTL_SZ_16384     0x00010000      /* rx buffer size 16384 */
-#define E1000_RCTL_SZ_8192      0x00020000      /* rx buffer size 8192 */
-#define E1000_RCTL_SZ_4096      0x00030000      /* rx buffer size 4096 */
-#define E1000_RCTL_VFE          0x00040000      /* vlan filter enable */
-#define E1000_RCTL_CFIEN        0x00080000      /* canonical form enable */
-#define E1000_RCTL_CFI          0x00100000      /* canonical form indicator */
-#define E1000_RCTL_DPF          0x00400000      /* discard pause frames */
-#define E1000_RCTL_PMCF         0x00800000      /* pass MAC control frames */
-#define E1000_RCTL_BSEX         0x02000000      /* Buffer size extension */
+#define E1000_RCTL_SZ_16384       0x00010000    /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192        0x00020000    /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096        0x00030000    /* rx buffer size 4096 */
+#define E1000_RCTL_VFE            0x00040000    /* vlan filter enable */
+#define E1000_RCTL_CFIEN          0x00080000    /* canonical form enable */
+#define E1000_RCTL_CFI            0x00100000    /* canonical form indicator */
+#define E1000_RCTL_DPF            0x00400000    /* discard pause frames */
+#define E1000_RCTL_PMCF           0x00800000    /* pass MAC control frames */
+#define E1000_RCTL_BSEX           0x02000000    /* Buffer size extension */
 
 /* Receive Descriptor */
 #define E1000_RDT_DELAY 0x0000ffff      /* Delay timer (1=1024us) */
@@ -1239,6 +1386,7 @@ struct e1000_hw {
 #define E1000_WUC_PME_EN     0x00000002 /* PME Enable */
 #define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */
 #define E1000_WUC_APMPME     0x00000008 /* Assert PME on APM Wakeup */
+#define E1000_WUC_SPM        0x80000000 /* Enable SPM */
 
 /* Wake Up Filter Control */
 #define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
@@ -1282,7 +1430,7 @@ struct e1000_hw {
 #define E1000_MANC_IPV6_EN       0x00000800 /* Enable IPv6 */
 #define E1000_MANC_SNAP_EN       0x00001000 /* Accept LLC/SNAP */
 #define E1000_MANC_ARP_EN        0x00002000 /* Enable ARP Request Filtering */
-#define E1000_MANC_NEIGHBOR_EN   0x00004000 /* Enable Neighbor Discovery 
+#define E1000_MANC_NEIGHBOR_EN   0x00004000 /* Enable Neighbor Discovery
                                              * Filtering */
 #define E1000_MANC_TCO_RESET     0x00010000 /* TCO Reset Occurred */
 #define E1000_MANC_RCV_TCO_EN    0x00020000 /* Receive TCO Packets Enabled */
@@ -1302,20 +1450,44 @@ struct e1000_hw {
 
 #define E1000_MDALIGN          4096
 
-/* EEPROM Commands */
-#define EEPROM_READ_OPCODE  0x6  /* EERPOM read opcode */
-#define EEPROM_WRITE_OPCODE 0x5  /* EERPOM write opcode */
-#define EEPROM_ERASE_OPCODE 0x7  /* EERPOM erase opcode */
-#define EEPROM_EWEN_OPCODE  0x13 /* EERPOM erase/write enable */
-#define EEPROM_EWDS_OPCODE  0x10 /* EERPOM erast/write disable */
+/* EEPROM Commands - Microwire */
+#define EEPROM_READ_OPCODE_MICROWIRE  0x6  /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5  /* EEPROM write opcode */
+#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7  /* EEPROM erase opcode */
+#define EEPROM_EWEN_OPCODE_MICROWIRE  0x13 /* EEPROM erase/write enable */
+#define EEPROM_EWDS_OPCODE_MICROWIRE  0x10 /* EEPROM erast/write disable */
+
+/* EEPROM Commands - SPI */
+#define EEPROM_MAX_RETRY_SPI    5000 /* Max wait of 5ms, for RDY signal */
+#define EEPROM_READ_OPCODE_SPI  0x3  /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_SPI 0x2  /* EEPROM write opcode */
+#define EEPROM_A8_OPCODE_SPI    0x8  /* opcode bit-3 = address bit-8 */
+#define EEPROM_WREN_OPCODE_SPI  0x6  /* EEPROM set Write Enable latch */
+#define EEPROM_WRDI_OPCODE_SPI  0x4  /* EEPROM reset Write Enable latch */
+#define EEPROM_RDSR_OPCODE_SPI  0x5  /* EEPROM read Status register */
+#define EEPROM_WRSR_OPCODE_SPI  0x1  /* EEPROM write Status register */
+
+/* EEPROM Size definitions */
+#define EEPROM_SIZE_16KB        0x1800
+#define EEPROM_SIZE_8KB         0x1400
+#define EEPROM_SIZE_4KB         0x1000
+#define EEPROM_SIZE_2KB         0x0C00
+#define EEPROM_SIZE_1KB         0x0800
+#define EEPROM_SIZE_512B        0x0400
+#define EEPROM_SIZE_128B        0x0000
+#define EEPROM_SIZE_MASK        0x1C00
 
 /* EEPROM Word Offsets */
-#define EEPROM_COMPAT              0x0003
-#define EEPROM_ID_LED_SETTINGS     0x0004
-#define EEPROM_INIT_CONTROL1_REG   0x000A
-#define EEPROM_INIT_CONTROL2_REG   0x000F
-#define EEPROM_FLASH_VERSION       0x0032
-#define EEPROM_CHECKSUM_REG        0x003F
+#define EEPROM_COMPAT                 0x0003
+#define EEPROM_ID_LED_SETTINGS        0x0004
+#define EEPROM_SERDES_AMPLITUDE       0x0006 /* For SERDES output amplitude adjustment. */
+#define EEPROM_INIT_CONTROL1_REG      0x000A
+#define EEPROM_INIT_CONTROL2_REG      0x000F
+#define EEPROM_INIT_CONTROL3_PORT_B   0x0014
+#define EEPROM_INIT_CONTROL3_PORT_A   0x0024
+#define EEPROM_CFG                    0x0012
+#define EEPROM_FLASH_VERSION          0x0032
+#define EEPROM_CHECKSUM_REG           0x003F
 
 /* Word definitions for ID LED Settings */
 #define ID_LED_RESERVED_0000 0x0000
@@ -1334,9 +1506,13 @@ struct e1000_hw {
 #define ID_LED_OFF1_ON2      0x8
 #define ID_LED_OFF1_OFF2     0x9
 
-/* Mask bits for fields in Word 0x03 of the EEPROM */
-#define EEPROM_COMPAT_SERVER 0x0400
-#define EEPROM_COMPAT_CLIENT 0x0200
+#define IGP_ACTIVITY_LED_MASK   0xFFFFF0FF
+#define IGP_ACTIVITY_LED_ENABLE 0x0300
+#define IGP_LED3_MODE           0x07000000
+
+
+/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */
+#define EEPROM_SERDES_AMPLITUDE_MASK  0x000F
 
 /* Mask bits for fields in Word 0x0a of the EEPROM */
 #define EEPROM_WORD0A_ILOS   0x0010
@@ -1359,6 +1535,8 @@ struct e1000_hw {
 #define EEPROM_NODE_ADDRESS_BYTE_0 0
 #define EEPROM_PBA_BYTE_1          8
 
+#define EEPROM_RESERVED_WORD          0xFFFF
+
 /* EEPROM Map Sizes (Byte Counts) */
 #define PBA_SIZE 4
 
@@ -1368,10 +1546,9 @@ struct e1000_hw {
 #define E1000_COLLISION_DISTANCE        64
 #define E1000_FDX_COLLISION_DISTANCE    E1000_COLLISION_DISTANCE
 #define E1000_HDX_COLLISION_DISTANCE    E1000_COLLISION_DISTANCE
-#define E1000_GB_HDX_COLLISION_DISTANCE 512
 #define E1000_COLD_SHIFT                12
 
-/* The number of Transmit and Receive Descriptors must be a multiple of 8 */
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
 #define REQ_TX_DESCRIPTOR_MULTIPLE  8
 #define REQ_RX_DESCRIPTOR_MULTIPLE  8
 
@@ -1409,7 +1586,9 @@ struct e1000_hw {
 
 /* PBA constants */
 #define E1000_PBA_16K 0x0010    /* 16KB, default TX allocation */
+#define E1000_PBA_22K 0x0016
 #define E1000_PBA_24K 0x0018
+#define E1000_PBA_30K 0x001E
 #define E1000_PBA_40K 0x0028
 #define E1000_PBA_48K 0x0030    /* 48KB, default RX allocation */
 
@@ -1436,35 +1615,30 @@ struct e1000_hw {
 #define PCIX_STATUS_HI_MMRBC_2K      0x2
 
 
-/* The number of bits that we need to shift right to move the "pause"
- * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field
- * in the TXCW register 
+/* Number of bits required to shift right the "pause" bits from the
+ * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register.
  */
 #define PAUSE_SHIFT 5
 
-/* The number of bits that we need to shift left to move the "SWDPIO"
- * bits from the EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field
- * in the CTRL register 
+/* Number of bits required to shift left the "SWDPIO" bits from the
+ * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register.
  */
 #define SWDPIO_SHIFT 17
 
-/* The number of bits that we need to shift left to move the "SWDPIO_EXT"
- * bits from the EEPROM word F (bits 7:4) to the bits 11:8 of The
- * Extended CTRL register.
- * in the CTRL register 
+/* Number of bits required to shift left the "SWDPIO_EXT" bits from the
+ * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register.
  */
 #define SWDPIO__EXT_SHIFT 4
 
-/* The number of bits that we need to shift left to move the "ILOS"
- * bit from the EEPROM (bit 4) to the "ILOS" (bit 7) field
- * in the CTRL register 
+/* Number of bits required to shift left the "ILOS" bit from the EEPROM
+ * (bit 4) to the "ILOS" (bit 7) field in the CTRL register.
  */
 #define ILOS_SHIFT  3
 
 
 #define RECEIVE_BUFFER_ALIGN_SIZE  (256)
 
-/* The number of milliseconds we wait for auto-negotiation to complete */
+/* Number of milliseconds we wait for auto-negotiation to complete */
 #define LINK_UP_TIMEOUT             500
 
 #define E1000_TX_BUFFER_SIZE ((uint32_t)1514)
@@ -1475,7 +1649,7 @@ struct e1000_hw {
 /* TBI_ACCEPT macro definition:
  *
  * This macro requires:
- *      adapter = a pointer to struct e1000_hw 
+ *      adapter = a pointer to struct e1000_hw
  *      status = the 8 bit status field of the RX descriptor with EOP set
  *      error = the 8 bit error field of the RX descriptor with EOP set
  *      length = the sum of all the length fields of the RX descriptors that
@@ -1484,7 +1658,7 @@ struct e1000_hw {
  *      max_frame_length = the maximum frame length we want to accept.
  *      min_frame_length = the minimum frame length we want to accept.
  *
- * This macro is a conditional that should be used in the interrupt 
+ * This macro is a conditional that should be used in the interrupt
  * handler's Rx processing routine when RxErrors have been detected.
  *
  * Typical use:
@@ -1547,8 +1721,60 @@ struct e1000_hw {
 #define M88E1000_EXT_PHY_SPEC_CTRL 0x14  /* Extended PHY Specific Control */
 #define M88E1000_RX_ERR_CNTR       0x15  /* Receive Error Counter */
 
-#define MAX_PHY_REG_ADDRESS 0x1F        /* 5 bit address bus (0-0x1F) */
+#define M88E1000_PHY_EXT_CTRL      0x1A  /* PHY extend control register */
+#define M88E1000_PHY_PAGE_SELECT   0x1D  /* Reg 29 for page number setting */
+#define M88E1000_PHY_GEN_CONTROL   0x1E  /* Its meaning depends on reg 29 */
+#define M88E1000_PHY_VCO_REG_BIT8  0x100 /* Bits 8 & 11 are adjusted for */
+#define M88E1000_PHY_VCO_REG_BIT11 0x800    /* improved BER performance */
+
+#define IGP01E1000_IEEE_REGS_PAGE  0x0000
+#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300
+#define IGP01E1000_IEEE_FORCE_GIGA      0x0140
+
+/* IGP01E1000 Specific Registers */
+#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */
+#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */
+#define IGP01E1000_PHY_PORT_CTRL   0x12 /* PHY Specific Control Register */
+#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */
+#define IGP01E1000_GMII_FIFO       0x14 /* GMII FIFO Register */
+#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */
+#define IGP01E1000_PHY_PAGE_SELECT     0x1F /* PHY Page Select Core Register */
+
+/* IGP01E1000 AGC Registers - stores the cable length values*/
+#define IGP01E1000_PHY_AGC_A        0x1172
+#define IGP01E1000_PHY_AGC_B        0x1272
+#define IGP01E1000_PHY_AGC_C        0x1472
+#define IGP01E1000_PHY_AGC_D        0x1872
+
+/* IGP01E1000 DSP Reset Register */
+#define IGP01E1000_PHY_DSP_RESET   0x1F33
+#define IGP01E1000_PHY_DSP_SET     0x1F71
+#define IGP01E1000_PHY_DSP_FFE     0x1F35
+
+#define IGP01E1000_PHY_CHANNEL_NUM    4
+#define IGP01E1000_PHY_AGC_PARAM_A    0x1171
+#define IGP01E1000_PHY_AGC_PARAM_B    0x1271
+#define IGP01E1000_PHY_AGC_PARAM_C    0x1471
+#define IGP01E1000_PHY_AGC_PARAM_D    0x1871
+
+#define IGP01E1000_PHY_EDAC_MU_INDEX        0xC000
+#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000
+
+#define IGP01E1000_PHY_ANALOG_TX_STATE      0x2890
+#define IGP01E1000_PHY_ANALOG_CLASS_A       0x2000
+#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE  0x0004
+#define IGP01E1000_PHY_DSP_FFE_CM_CP        0x0069
+
+#define IGP01E1000_PHY_DSP_FFE_DEFAULT      0x002A
+/* IGP01E1000 PCS Initialization register - stores the polarity status when
+ * speed = 1000 Mbps. */
+#define IGP01E1000_PHY_PCS_INIT_REG  0x00B4
+#define IGP01E1000_PHY_PCS_CTRL_REG  0x00B5
+
+#define IGP01E1000_ANALOG_REGS_PAGE  0x20C0
 
+#define MAX_PHY_REG_ADDRESS 0x1F        /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG  0xF     /*Registers that are equal on all pages*/
 /* PHY Control Register */
 #define MII_CR_SPEED_SELECT_MSB 0x0040  /* bits 6,13: 10=1000, 01=100, 00=10 */
 #define MII_CR_COLL_TEST_ENABLE 0x0080  /* Collision test enable */
@@ -1608,7 +1834,7 @@ struct e1000_hw {
 #define NWAY_ER_PAGE_RXD          0x0002 /* LP is 10T   Half Duplex Capable */
 #define NWAY_ER_NEXT_PAGE_CAPS    0x0004 /* LP is 10T   Full Duplex Capable */
 #define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */
-#define NWAY_ER_PAR_DETECT_FAULT  0x0100 /* LP is 100TX Full Duplex Capable */
+#define NWAY_ER_PAR_DETECT_FAULT  0x0010 /* LP is 100TX Full Duplex Capable */
 
 /* Next Page TX Register */
 #define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
@@ -1619,7 +1845,7 @@ struct e1000_hw {
                                     * 0 = cannot comply with msg
                                     */
 #define NPTX_MSG_PAGE       0x2000 /* formatted(1)/unformatted(0) pg */
-#define NPTX_NEXT_PAGE      0x8000 /* 1 = addition NP will follow 
+#define NPTX_NEXT_PAGE      0x8000 /* 1 = addition NP will follow
                                     * 0 = sending last NP
                                     */
 
@@ -1628,13 +1854,13 @@ struct e1000_hw {
 #define LP_RNPR_TOGGLE         0x0800 /* Toggles between exchanges
                                        * of different NP
                                        */
-#define LP_RNPR_ACKNOWLDGE2    0x1000 /* 1 = will comply with msg 
+#define LP_RNPR_ACKNOWLDGE2    0x1000 /* 1 = will comply with msg
                                        * 0 = cannot comply with msg
                                        */
 #define LP_RNPR_MSG_PAGE       0x2000  /* formatted(1)/unformatted(0) pg */
 #define LP_RNPR_ACKNOWLDGE     0x4000  /* 1 = ACK / 0 = NO ACK */
 #define LP_RNPR_NEXT_PAGE      0x8000  /* 1 = addition NP will follow
-                                        * 0 = sending last NP 
+                                        * 0 = sending last NP
                                         */
 
 /* 1000BASE-T Control Register */
@@ -1662,8 +1888,11 @@ struct e1000_hw {
 #define SR_1000T_LOCAL_RX_STATUS  0x2000 /* Local receiver OK */
 #define SR_1000T_MS_CONFIG_RES    0x4000 /* 1=Local TX is Master, 0=Slave */
 #define SR_1000T_MS_CONFIG_FAULT  0x8000 /* Master/Slave config fault */
-#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12
-#define SR_1000T_LOCAL_RX_STATUS_SHIFT  13
+#define SR_1000T_REMOTE_RX_STATUS_SHIFT          12
+#define SR_1000T_LOCAL_RX_STATUS_SHIFT           13
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT    5
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20            20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100           100
 
 /* Extended Status Register */
 #define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
@@ -1681,20 +1910,20 @@ struct e1000_hw {
 #define M88E1000_PSCR_JABBER_DISABLE    0x0001 /* 1=Jabber Function disabled */
 #define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
 #define M88E1000_PSCR_SQE_TEST          0x0004 /* 1=SQE Test enabled */
-#define M88E1000_PSCR_CLK125_DISABLE    0x0010 /* 1=CLK125 low, 
+#define M88E1000_PSCR_CLK125_DISABLE    0x0010 /* 1=CLK125 low,
                                                 * 0=CLK125 toggling
                                                 */
 #define M88E1000_PSCR_MDI_MANUAL_MODE  0x0000  /* MDI Crossover Mode bits 6:5 */
                                                /* Manual MDI configuration */
 #define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020  /* Manual MDIX configuration */
 #define M88E1000_PSCR_AUTO_X_1000T     0x0040  /* 1000BASE-T: Auto crossover,
-                                                *  100BASE-TX/10BASE-T: 
+                                                *  100BASE-TX/10BASE-T:
                                                 *  MDI Mode
                                                 */
-#define M88E1000_PSCR_AUTO_X_MODE      0x0060  /* Auto crossover enabled 
-                                                * all speeds. 
+#define M88E1000_PSCR_AUTO_X_MODE      0x0060  /* Auto crossover enabled
+                                                * all speeds.
                                                 */
-#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 
+#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080
                                         /* 1=Enable Extended 10BASE-T distance
                                          * (Lower 10BASE-T RX Threshold)
                                          * 0=Normal 10BASE-T RX Threshold */
@@ -1712,6 +1941,7 @@ struct e1000_hw {
 /* M88E1000 PHY Specific Status Register */
 #define M88E1000_PSSR_JABBER             0x0001 /* 1=Jabber */
 #define M88E1000_PSSR_REV_POLARITY       0x0002 /* 1=Polarity reversed */
+#define M88E1000_PSSR_DOWNSHIFT          0x0020 /* 1=Downshifted */
 #define M88E1000_PSSR_MDIX               0x0040 /* 1=MDIX; 0=MDI */
 #define M88E1000_PSSR_CABLE_LENGTH       0x0380 /* 0=<50M;1=50-80M;2=80-110M;
                                             * 3=110-140M;4=>140M */
@@ -1725,6 +1955,7 @@ struct e1000_hw {
 #define M88E1000_PSSR_1000MBS            0x8000 /* 10=1000Mbs */
 
 #define M88E1000_PSSR_REV_POLARITY_SHIFT 1
+#define M88E1000_PSSR_DOWNSHIFT_SHIFT    5
 #define M88E1000_PSSR_MDIX_SHIFT         6
 #define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7
 
@@ -1733,12 +1964,12 @@ struct e1000_hw {
 #define M88E1000_EPSCR_DOWN_NO_IDLE   0x8000 /* 1=Lost lock detect enabled.
                                               * Will assert lost lock and bring
                                               * link down if idle not seen
-                                              * within 1ms in 1000BASE-T 
+                                              * within 1ms in 1000BASE-T
                                               */
 /* Number of times we will attempt to autonegotiate before downshifting if we
  * are the master */
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00
-#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X   0x0000    
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X   0x0000
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X   0x0400
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X   0x0800
 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X   0x0C00
@@ -1753,10 +1984,102 @@ struct e1000_hw {
 #define M88E1000_EPSCR_TX_CLK_25      0x0070 /* 25  MHz TX_CLK */
 #define M88E1000_EPSCR_TX_CLK_0       0x0000 /* NO  TX_CLK */
 
+/* IGP01E1000 Specific Port Config Register - R/W */
+#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT  0x0010
+#define IGP01E1000_PSCFR_PRE_EN                0x0020
+#define IGP01E1000_PSCFR_SMART_SPEED           0x0080
+#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK    0x0100
+#define IGP01E1000_PSCFR_DISABLE_JABBER        0x0400
+#define IGP01E1000_PSCFR_DISABLE_TRANSMIT      0x2000
+
+/* IGP01E1000 Specific Port Status Register - R/O */
+#define IGP01E1000_PSSR_AUTONEG_FAILED         0x0001 /* RO LH SC */
+#define IGP01E1000_PSSR_POLARITY_REVERSED      0x0002
+#define IGP01E1000_PSSR_CABLE_LENGTH           0x007C
+#define IGP01E1000_PSSR_FULL_DUPLEX            0x0200
+#define IGP01E1000_PSSR_LINK_UP                0x0400
+#define IGP01E1000_PSSR_MDIX                   0x0800
+#define IGP01E1000_PSSR_SPEED_MASK             0xC000 /* speed bits mask */
+#define IGP01E1000_PSSR_SPEED_10MBPS           0x4000
+#define IGP01E1000_PSSR_SPEED_100MBPS          0x8000
+#define IGP01E1000_PSSR_SPEED_1000MBPS         0xC000
+#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT     0x0002 /* shift right 2 */
+#define IGP01E1000_PSSR_MDIX_SHIFT             0x000B /* shift right 11 */
+
+/* IGP01E1000 Specific Port Control Register - R/W */
+#define IGP01E1000_PSCR_TP_LOOPBACK            0x0001
+#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR      0x0200
+#define IGP01E1000_PSCR_TEN_CRS_SELECT         0x0400
+#define IGP01E1000_PSCR_FLIP_CHIP              0x0800
+#define IGP01E1000_PSCR_AUTO_MDIX              0x1000
+#define IGP01E1000_PSCR_FORCE_MDI_MDIX         0x2000 /* 0-MDI, 1-MDIX */
+
+/* IGP01E1000 Specific Port Link Health Register */
+#define IGP01E1000_PLHR_SS_DOWNGRADE           0x8000
+#define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR    0x4000
+#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK       0x0800 /* LH */
+#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW   0x0400 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_1             0x0200 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_0             0x0100
+#define IGP01E1000_PLHR_AUTONEG_FAULT          0x0010
+#define IGP01E1000_PLHR_AUTONEG_ACTIVE         0x0008
+#define IGP01E1000_PLHR_VALID_CHANNEL_D        0x0004
+#define IGP01E1000_PLHR_VALID_CHANNEL_C        0x0002
+#define IGP01E1000_PLHR_VALID_CHANNEL_B        0x0001
+#define IGP01E1000_PLHR_VALID_CHANNEL_A        0x0000
+
+/* IGP01E1000 Channel Quality Register */
+#define IGP01E1000_MSE_CHANNEL_D        0x000F
+#define IGP01E1000_MSE_CHANNEL_C        0x00F0
+#define IGP01E1000_MSE_CHANNEL_B        0x0F00
+#define IGP01E1000_MSE_CHANNEL_A        0xF000
+
+/* IGP01E1000 DSP reset macros */
+#define DSP_RESET_ENABLE     0x0
+#define DSP_RESET_DISABLE    0x2
+#define E1000_MAX_DSP_RESETS 10
+
+/* IGP01E1000 AGC Registers */
+
+#define IGP01E1000_AGC_LENGTH_SHIFT 7         /* Coarse - 13:11, Fine - 10:7 */
+
+/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */
+#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128
+
+/* The precision of the length is +/- 10 meters */
+#define IGP01E1000_AGC_RANGE    10
+
+/* IGP01E1000 PCS Initialization register */
+/* bits 3:6 in the PCS registers stores the channels polarity */
+#define IGP01E1000_PHY_POLARITY_MASK    0x0078
+
+/* IGP01E1000 GMII FIFO Register */
+#define IGP01E1000_GMII_FLEX_SPD               0x10 /* Enable flexible speed
+                                                     * on Link-Up */
+#define IGP01E1000_GMII_SPD                    0x20 /* Enable SPD */
+
+/* IGP01E1000 Analog Register */
+#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS       0x20D1
+#define IGP01E1000_ANALOG_FUSE_STATUS             0x20D0
+#define IGP01E1000_ANALOG_FUSE_CONTROL            0x20DC
+#define IGP01E1000_ANALOG_FUSE_BYPASS             0x20DE
+
+#define IGP01E1000_ANALOG_FUSE_POLY_MASK            0xF000
+#define IGP01E1000_ANALOG_FUSE_FINE_MASK            0x0F80
+#define IGP01E1000_ANALOG_FUSE_COARSE_MASK          0x0070
+#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED        0x0100
+#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL    0x0002
+
+#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH        0x0040
+#define IGP01E1000_ANALOG_FUSE_COARSE_10            0x0010
+#define IGP01E1000_ANALOG_FUSE_FINE_1               0x0080
+#define IGP01E1000_ANALOG_FUSE_FINE_10              0x0500
+
 /* Bit definitions for valid PHY IDs. */
 #define M88E1000_E_PHY_ID  0x01410C50
 #define M88E1000_I_PHY_ID  0x01410C30
 #define M88E1011_I_PHY_ID  0x01410C20
+#define IGP01E1000_I_PHY_ID  0x02A80380
 #define M88E1000_12_PHY_ID M88E1000_E_PHY_ID
 #define M88E1000_14_PHY_ID M88E1000_E_PHY_ID
 #define M88E1011_I_REV_4   0x04
@@ -1785,5 +2108,14 @@ struct e1000_hw {
 #define ADVERTISE_1000_HALF 0x0010
 #define ADVERTISE_1000_FULL 0x0020
 #define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F  /* Everything but 1000-Half */
+#define AUTONEG_ADVERTISE_10_100_ALL    0x000F /* All 10/100 speeds*/
+#define AUTONEG_ADVERTISE_10_ALL        0x0003 /* 10Mbps Full & Half speeds*/
+
+#define TANAX_TTL_WA_RESET(hw) {                                       \
+    if((hw)->dsp_reset_counter) {                                      \
+        e1000_write_phy_reg((hw), IGP01E1000_PHY_DSP_RESET, 0x0000);   \
+        (hw)->dsp_reset_counter = 0;                                   \
+    }                                                                  \
+}
 
 #endif /* _E1000_HW_H_ */
index dd0de338f0d193b02644bbb4c1bc61071e99a909..4d88a61465185838c89830ebdffd1e4c6d337a5f 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
 
 /* Change Log
  *
- * 4.4.19       11/27/02
- *   o Feature: Added user-settable knob for interrupt throttle rate (ITR).
- *   o Cleanup: removed large static array allocations.
- *   o Cleanup: C99 struct initializer format.
- *   o Bug fix: restore VLAN settings when interface is brought up.
- *   o Bug fix: return cleanly in probe if error in detecting MAC type.
- *   o Bug fix: Wake up on magic packet by default only if enabled in eeprom.
- *   o Bug fix: Validate MAC address in set_mac.
- *   o Bug fix: Throw away zero-length Tx skbs.
- *   o Bug fix: Make ethtool EEPROM acceses work on older versions of ethtool.
- * 
- * 4.4.12       10/15/02
- *   o Clean up: use members of pci_device rather than direct calls to
- *     pci_read_config_word.
- *   o Bug fix: changed default flow control settings.
- *   o Clean up: ethtool file now has an inclusive list for adapters in the
- *     Wake-On-LAN capabilities instead of an exclusive list.
- *   o Bug fix: miscellaneous WoL bug fixes.
- *   o Added software interrupt for clearing rx ring
- *   o Bug fix: easier to undo "forcing" of 1000/fd using ethtool.
- *   o Now setting netdev->mem_end in e1000_probe.
- *   o Clean up: Moved tx_timeout from interrupt context to process context
- *     using schedule_task.
- * 
- * 4.3.15       8/9/02
+ * 5.2.16      8/8/03
+ *   o Added support for new controllers: 82545GM, 82546GB, 82541/7_B1
+ *   o Bug fix: reset h/w before first EEPROM read because we don't know
+ *     who may have been messing with the device before we got there.
+ *     [Dave Johnson (ddj -a-t- cascv.brown.edu)]
+ *   o Bug fix: read the correct work from EEPROM to detect programmed
+ *     WoL settings.
+ *   o Bug fix: TSO would hang if space left in FIFO was being miscalculated
+ *     when mss dropped without a correspoding drop in the DMA buffer size.
+ *   o ASF for Fiber nics isn't supported.
+ *   o Bug fix: Workaround added for potential hang with 82544 running in
+ *     PCI-X if send buffer terminates within an evenly-aligned dword.
+ *   o Feature: Add support for ethtool flow control setting.
+ *   o Feature: Add support for ethtool TSO setting.
+ *   o Feature: Increase default Tx Descriptor count to 1024 for >= 82544.
+ *   
+ * 5.1.13      5/28/03
+ *   o Bug fix: request_irq() failure resulted in freeing resources twice!
+ *     [Don Fry (brazilnut@us.ibm.com)]
+ *   o Bug fix: fix VLAN support on ppc64 [Mark Rakes (mrakes@vivato.net)]
+ *   o Bug fix: missing Tx cleanup opportunities during interrupt handling.
+ *   o Bug fix: alloc_etherdev failure didn't cleanup regions in probe.
+ *   o Cleanup: s/int/unsigned int/ for descriptor ring indexes.
+ *   
+ * 5.1.11      5/6/03
  */
 
 char e1000_driver_name[] = "e1000";
 char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
-char e1000_driver_version[] = "4.4.19-k2";
-char e1000_copyright[] = "Copyright (c) 1999-2002 Intel Corporation.";
+char e1000_driver_version[] = "5.2.16";
+char e1000_copyright[] = "Copyright (c) 1999-2003 Intel Corporation.";
 
 /* e1000_pci_tbl - PCI Device ID Table
  *
- * Private driver_data field (last one) stores an index into e1000_strings
  * Wildcard entries (PCI_ANY_ID) should come last
  * Last entry must be all 0s
  *
  * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
- *   Class, Class Mask, String Index }
+ *   Class, Class Mask, private data (not used) }
  */
 static struct pci_device_id e1000_pci_tbl[] __devinitdata = {
-       /* Intel(R) PRO/1000 Network Connection */
-       {0x8086, 0x1000, 0x8086, 0x1000, 0, 0, 0},
-       {0x8086, 0x1001, 0x8086, 0x1003, 0, 0, 0},
-       {0x8086, 0x1004, 0x8086, 0x1004, 0, 0, 0},
-       {0x8086, 0x1008, 0x8086, 0x1107, 0, 0, 0},
-       {0x8086, 0x1009, 0x8086, 0x1109, 0, 0, 0},
-       {0x8086, 0x100C, 0x8086, 0x1112, 0, 0, 0},
-       {0x8086, 0x100E, 0x8086, 0x001E, 0, 0, 0},
-       /* Compaq Gigabit Ethernet Server Adapter */
-       {0x8086, 0x1000, 0x0E11, PCI_ANY_ID, 0, 0, 1},
-       {0x8086, 0x1001, 0x0E11, PCI_ANY_ID, 0, 0, 1},
-       {0x8086, 0x1004, 0x0E11, PCI_ANY_ID, 0, 0, 1},
-       /* IBM Mobile, Desktop & Server Adapters */
-       {0x8086, 0x1000, 0x1014, PCI_ANY_ID, 0, 0, 2},
-       {0x8086, 0x1001, 0x1014, PCI_ANY_ID, 0, 0, 2},
-       {0x8086, 0x1004, 0x1014, PCI_ANY_ID, 0, 0, 2},
-       /* Generic */
        {0x8086, 0x1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -98,34 +80,45 @@ static struct pci_device_id e1000_pci_tbl[] __devinitdata = {
        {0x8086, 0x100D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x100E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x100F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-       {0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1015, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1016, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x1017, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x101D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1026, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1027, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1075, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1076, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1077, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1078, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x1079, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x107A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x8086, 0x107B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        /* required last entry */
        {0,}
 };
 
 MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
 
-static char *e1000_strings[] = {
-       "Intel(R) PRO/1000 Network Connection",
-       "Compaq Gigabit Ethernet Server Adapter",
-       "IBM Mobile, Desktop & Server Adapters"
-};
-
 /* Local Function Prototypes */
 
 int e1000_up(struct e1000_adapter *adapter);
 void e1000_down(struct e1000_adapter *adapter);
 void e1000_reset(struct e1000_adapter *adapter);
+int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
 
 static int e1000_init_module(void);
 static void e1000_exit_module(void);
 static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
-static void e1000_remove(struct pci_dev *pdev);
+static void __devexit e1000_remove(struct pci_dev *pdev);
 static int e1000_sw_init(struct e1000_adapter *adapter);
 static int e1000_open(struct net_device *netdev);
 static int e1000_close(struct net_device *netdev);
@@ -141,6 +134,7 @@ static void e1000_free_rx_resources(struct e1000_adapter *adapter);
 static void e1000_set_multi(struct net_device *netdev);
 static void e1000_update_phy_info(unsigned long data);
 static void e1000_watchdog(unsigned long data);
+static void e1000_82547_tx_fifo_stall(unsigned long data);
 static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
 static struct net_device_stats * e1000_get_stats(struct net_device *netdev);
 static int e1000_change_mtu(struct net_device *netdev, int new_mtu);
@@ -148,11 +142,21 @@ static int e1000_set_mac(struct net_device *netdev, void *p);
 static void e1000_update_stats(struct e1000_adapter *adapter);
 static inline void e1000_irq_disable(struct e1000_adapter *adapter);
 static inline void e1000_irq_enable(struct e1000_adapter *adapter);
-static void e1000_intr(int irq, void *data, struct pt_regs *regs);
-static void e1000_clean_tx_irq(struct e1000_adapter *adapter);
-static void e1000_clean_rx_irq(struct e1000_adapter *adapter);
+static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs);
+static boolean_t e1000_clean_tx_irq(struct e1000_adapter *adapter);
+#ifdef CONFIG_E1000_NAPI
+static int e1000_clean(struct net_device *netdev, int *budget);
+static boolean_t e1000_clean_rx_irq(struct e1000_adapter *adapter,
+                                    int *work_done, int work_to_do);
+#else
+static boolean_t e1000_clean_rx_irq(struct e1000_adapter *adapter);
+#endif
 static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter);
 static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+#ifdef SIOCGMIIPHY
+static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
+                          int cmd);
+#endif
 static void e1000_enter_82542_rst(struct e1000_adapter *adapter);
 static void e1000_leave_82542_rst(struct e1000_adapter *adapter);
 static inline void e1000_rx_checksum(struct e1000_adapter *adapter,
@@ -160,11 +164,16 @@ static inline void e1000_rx_checksum(struct e1000_adapter *adapter,
                                      struct sk_buff *skb);
 static void e1000_tx_timeout(struct net_device *dev);
 static void e1000_tx_timeout_task(struct net_device *dev);
+static void e1000_smartspeed(struct e1000_adapter *adapter);
+static inline int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
+                                             struct sk_buff *skb);
 
+#ifdef NETIF_F_HW_VLAN_TX
 static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
 static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid);
 static void e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
 static void e1000_restore_vlan(struct e1000_adapter *adapter);
+#endif
 
 static int e1000_notify_reboot(struct notifier_block *, unsigned long event, void *ptr);
 static int e1000_suspend(struct pci_dev *pdev, uint32_t state);
@@ -210,17 +219,15 @@ static int __init
 e1000_init_module(void)
 {
        int ret;
-
-#if 0 /* Avoid disconcerting noise. */
        printk(KERN_INFO "%s - version %s\n",
               e1000_driver_string, e1000_driver_version);
 
        printk(KERN_INFO "%s\n", e1000_copyright);
-#endif
 
        ret = pci_module_init(&e1000_driver);
-//     if(ret >= 0)
-//             register_reboot_notifier(&e1000_notifier_reboot);
+       if(ret >= 0) {
+               //register_reboot_notifier(&e1000_notifier_reboot);
+       }
        return ret;
 }
 
@@ -236,7 +243,7 @@ module_init(e1000_init_module);
 static void __exit
 e1000_exit_module(void)
 {
-//     unregister_reboot_notifier(&e1000_notifier_reboot);
+       unregister_reboot_notifier(&e1000_notifier_reboot);
        pci_unregister_driver(&e1000_driver);
 }
 
@@ -248,20 +255,23 @@ e1000_up(struct e1000_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
 
-       if(request_irq(netdev->irq, &e1000_intr, SA_SHIRQ | SA_SAMPLE_RANDOM,
-                      netdev->name, netdev))
-               return -1;
-
        /* hardware has been reset, we need to reload some things */
 
        e1000_set_multi(netdev);
+
+#ifdef NETIF_F_HW_VLAN_TX
        e1000_restore_vlan(adapter);
+#endif
 
        e1000_configure_tx(adapter);
        e1000_setup_rctl(adapter);
        e1000_configure_rx(adapter);
        e1000_alloc_rx_buffers(adapter);
 
+       if(request_irq(netdev->irq, &e1000_intr, SA_SHIRQ | SA_SAMPLE_RANDOM,
+                      netdev->name, netdev))
+               return -1;
+
        mod_timer(&adapter->watchdog_timer, jiffies);
        e1000_irq_enable(adapter);
 
@@ -275,6 +285,7 @@ e1000_down(struct e1000_adapter *adapter)
 
        e1000_irq_disable(adapter);
        free_irq(netdev->irq, netdev);
+       del_timer_sync(&adapter->tx_fifo_stall_timer);
        del_timer_sync(&adapter->watchdog_timer);
        del_timer_sync(&adapter->phy_info_timer);
        adapter->link_speed = 0;
@@ -290,14 +301,28 @@ e1000_down(struct e1000_adapter *adapter)
 void
 e1000_reset(struct e1000_adapter *adapter)
 {
+       uint32_t pba;
        /* Repartition Pba for greater than 9k mtu
         * To take effect CTRL.RST is required.
         */
 
-       if(adapter->rx_buffer_len > E1000_RXBUFFER_8192)
-               E1000_WRITE_REG(&adapter->hw, PBA, E1000_JUMBO_PBA);
-       else
-               E1000_WRITE_REG(&adapter->hw, PBA, E1000_DEFAULT_PBA);
+       if(adapter->hw.mac_type < e1000_82547) {
+               if(adapter->rx_buffer_len > E1000_RXBUFFER_8192)
+                       pba = E1000_PBA_40K;
+               else
+                       pba = E1000_PBA_48K;
+       } else {
+               if(adapter->rx_buffer_len > E1000_RXBUFFER_8192)
+                       pba = E1000_PBA_22K;
+               else
+                       pba = E1000_PBA_30K;
+               adapter->tx_fifo_head = 0;
+               adapter->tx_head_addr = pba << E1000_TX_HEAD_ADDR_SHIFT;
+               adapter->tx_fifo_size =
+                       (E1000_PBA_40K - pba) << E1000_TX_FIFO_SIZE_SHIFT;
+               atomic_set(&adapter->tx_fifo_stall, 0);
+       }
+       E1000_WRITE_REG(&adapter->hw, PBA, pba);
 
        adapter->hw.fc = adapter->hw.original_fc;
        e1000_reset_hw(&adapter->hw);
@@ -356,13 +381,13 @@ e1000_probe(struct pci_dev *pdev,
                goto err_alloc_etherdev;
 
        SET_MODULE_OWNER(netdev);
+       SET_NETDEV_DEV(netdev, &pdev->dev);
 
        pci_set_drvdata(pdev, netdev);
        adapter = netdev->priv;
        adapter->netdev = netdev;
        adapter->pdev = pdev;
        adapter->hw.back = adapter;
-       spin_lock_init(&adapter->tx_lock);
 
        mmio_start = pci_resource_start(pdev, BAR_0);
        mmio_len = pci_resource_len(pdev, BAR_0);
@@ -388,11 +413,19 @@ e1000_probe(struct pci_dev *pdev,
        netdev->set_mac_address = &e1000_set_mac;
        netdev->change_mtu = &e1000_change_mtu;
        netdev->do_ioctl = &e1000_ioctl;
+#ifdef HAVE_TX_TIMEOUT
        netdev->tx_timeout = &e1000_tx_timeout;
-       netdev->watchdog_timeo = HZ;
+       netdev->watchdog_timeo = 5 * HZ;
+#endif
+#ifdef CONFIG_E1000_NAPI
+       netdev->poll = &e1000_clean;
+       netdev->weight = 64;
+#endif
+#ifdef NETIF_F_HW_VLAN_TX
        netdev->vlan_rx_register = e1000_vlan_rx_register;
        netdev->vlan_rx_add_vid = e1000_vlan_rx_add_vid;
        netdev->vlan_rx_kill_vid = e1000_vlan_rx_kill_vid;
+#endif
 
        netdev->irq = pdev->irq;
        netdev->mem_start = mmio_start;
@@ -400,25 +433,41 @@ e1000_probe(struct pci_dev *pdev,
        netdev->base_addr = adapter->hw.io_base;
 
        adapter->bd_number = cards_found;
-       adapter->id_string = e1000_strings[ent->driver_data];
 
        /* setup the private structure */
 
        if(e1000_sw_init(adapter))
                goto err_sw_init;
 
+#ifdef MAX_SKB_FRAGS
        if(adapter->hw.mac_type >= e1000_82543) {
+#ifdef NETIF_F_HW_VLAN_TX
                netdev->features = NETIF_F_SG |
-                                  NETIF_F_HW_CSUM |
-                                  NETIF_F_HW_VLAN_TX |
-                                  NETIF_F_HW_VLAN_RX |
+                                  NETIF_F_HW_CSUM |
+                                  NETIF_F_HW_VLAN_TX |
+                                  NETIF_F_HW_VLAN_RX |
                                   NETIF_F_HW_VLAN_FILTER;
+#else
+               netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM;
+#endif
        } else {
                netdev->features = NETIF_F_SG;
        }
 
+#ifdef NETIF_F_TSO
+       if((adapter->hw.mac_type >= e1000_82544) &&
+          (adapter->hw.mac_type != e1000_82547))
+               netdev->features |= NETIF_F_TSO;
+#endif
+
        if(pci_using_dac)
                netdev->features |= NETIF_F_HIGHDMA;
+#endif
+
+       /* before reading the EEPROM, reset the controller to 
+        * put the device in a known good starting state */
+       
+       e1000_reset_hw(&adapter->hw);
 
        /* make sure the EEPROM is good */
 
@@ -439,13 +488,9 @@ e1000_probe(struct pci_dev *pdev,
 
        e1000_get_bus_info(&adapter->hw);
 
-       if((adapter->hw.mac_type == e1000_82544) &&
-          (adapter->hw.bus_type == e1000_bus_type_pcix))
-
-               adapter->max_data_per_txd = 4096;
-       else
-               adapter->max_data_per_txd = MAX_JUMBO_FRAME_SIZE;
-
+       init_timer(&adapter->tx_fifo_stall_timer);
+       adapter->tx_fifo_stall_timer.function = &e1000_82547_tx_fifo_stall;
+       adapter->tx_fifo_stall_timer.data = (unsigned long) adapter;
 
        init_timer(&adapter->watchdog_timer);
        adapter->watchdog_timer.function = &e1000_watchdog;
@@ -455,33 +500,51 @@ e1000_probe(struct pci_dev *pdev,
        adapter->phy_info_timer.function = &e1000_update_phy_info;
        adapter->phy_info_timer.data = (unsigned long) adapter;
 
-       INIT_TQUEUE(&adapter->tx_timeout_task,
+       INIT_WORK(&adapter->tx_timeout_task,
                (void (*)(void *))e1000_tx_timeout_task, netdev);
 
        register_netdev(netdev);
-       memcpy(adapter->ifname, netdev->name, IFNAMSIZ);
-       adapter->ifname[IFNAMSIZ-1] = 0;
 
        /* we're going to reset, so assume we have no link for now */
 
        netif_carrier_off(netdev);
        netif_stop_queue(netdev);
 
-       printk(KERN_INFO "%s: %s\n", netdev->name, adapter->id_string);
+       printk(KERN_INFO "%s: Intel(R) PRO/1000 Network Connection\n",
+              netdev->name);
        e1000_check_options(adapter);
+
        /* Initial Wake on LAN setting
         * If APM wake is enabled in the EEPROM,
         * enable the ACPI Magic Packet filter
         */
 
-       e1000_read_eeprom(&adapter->hw, EEPROM_INIT_CONTROL2_REG, &eeprom_data);
-       if((adapter->hw.mac_type >= e1000_82544) &&
-          (eeprom_data & E1000_EEPROM_APME))
+       switch(adapter->hw.mac_type) {
+       case e1000_82542_rev2_0:
+       case e1000_82542_rev2_1:
+       case e1000_82543:
+               break;
+       case e1000_82546:
+       case e1000_82546_rev_3:
+               if((E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_FUNC_1)
+                  && (adapter->hw.media_type == e1000_media_type_copper)) {
+                       e1000_read_eeprom(&adapter->hw,
+                               EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+                       break;
+               }
+               /* Fall Through */
+       default:
+               e1000_read_eeprom(&adapter->hw,
+                       EEPROM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+               break;
+       }
+       if(eeprom_data & E1000_EEPROM_APME)
                adapter->wol |= E1000_WUFC_MAG;
 
        /* reset the hardware with the new settings */
 
        e1000_reset(adapter);
+
        cards_found++;
        return 0;
 
@@ -489,9 +552,9 @@ err_sw_init:
 err_eeprom:
        iounmap(adapter->hw.hw_addr);
 err_ioremap:
-       pci_release_regions(pdev);
        kfree(netdev);
 err_alloc_etherdev:
+       pci_release_regions(pdev);
        return -ENOMEM;
 }
 
@@ -512,7 +575,8 @@ e1000_remove(struct pci_dev *pdev)
        struct e1000_adapter *adapter = netdev->priv;
        uint32_t manc;
 
-       if(adapter->hw.mac_type >= e1000_82540) {
+       if(adapter->hw.mac_type >= e1000_82540 &&
+          adapter->hw.media_type == e1000_media_type_copper) {
                manc = E1000_READ_REG(&adapter->hw, MANC);
                if(manc & E1000_MANC_SMBUS_EN) {
                        manc |= E1000_MANC_ARP_EN;
@@ -559,7 +623,7 @@ e1000_sw_init(struct e1000_adapter *adapter)
 
        adapter->rx_buffer_len = E1000_RXBUFFER_2048;
        hw->max_frame_size = netdev->mtu +
-                                ENET_HEADER_SIZE + ETHERNET_FCS_SIZE;
+                            ENET_HEADER_SIZE + ETHERNET_FCS_SIZE;
        hw->min_frame_size = MINIMUM_ETHERNET_FRAME_SIZE;
 
        /* identify the MAC */
@@ -569,6 +633,10 @@ e1000_sw_init(struct e1000_adapter *adapter)
                return -1;
        }
 
+       /* initialize eeprom parameters */
+
+       e1000_init_eeprom_params(hw);
+
        /* flow control settings */
 
        hw->fc_high_water = E1000_FC_HIGH_THRESH;
@@ -576,18 +644,13 @@ e1000_sw_init(struct e1000_adapter *adapter)
        hw->fc_pause_time = E1000_FC_PAUSE_TIME;
        hw->fc_send_xon = 1;
 
-       /* Media type - copper or fiber */
-
-       if(hw->mac_type >= e1000_82543) {
-               uint32_t status = E1000_READ_REG(hw, STATUS);
+       if((hw->mac_type == e1000_82541) ||
+          (hw->mac_type == e1000_82547) ||
+          (hw->mac_type == e1000_82541_rev_2) ||
+          (hw->mac_type == e1000_82547_rev_2))
+               hw->phy_init_script = 1;
 
-               if(status & E1000_STATUS_TBIMODE)
-                       hw->media_type = e1000_media_type_fiber;
-               else
-                       hw->media_type = e1000_media_type_copper;
-       } else {
-               hw->media_type = e1000_media_type_fiber;
-       }
+       e1000_set_media_type(hw);
 
        if(hw->mac_type < e1000_82543)
                hw->report_tx_early = 0;
@@ -603,6 +666,7 @@ e1000_sw_init(struct e1000_adapter *adapter)
        if(hw->media_type == e1000_media_type_copper) {
                hw->mdix = AUTO_ALL_MODES;
                hw->disable_polarity_correction = FALSE;
+               hw->master_slave = E1000_MASTER_SLAVE;
        }
 
        atomic_set(&adapter->irq_sem, 1);
@@ -752,7 +816,8 @@ e1000_configure_tx(struct e1000_adapter *adapter)
                tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
                break;
        default:
-               if(adapter->hw.media_type == e1000_media_type_fiber)
+               if(adapter->hw.media_type == e1000_media_type_fiber ||
+                  adapter->hw.media_type == e1000_media_type_internal_serdes)
                        tipg = DEFAULT_82543_TIPG_IPGT_FIBER;
                else
                        tipg = DEFAULT_82543_TIPG_IPGT_COPPER;
@@ -773,19 +838,26 @@ e1000_configure_tx(struct e1000_adapter *adapter)
 
        tctl &= ~E1000_TCTL_CT;
        tctl |= E1000_TCTL_EN | E1000_TCTL_PSP |
-              (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
+               (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
 
        E1000_WRITE_REG(&adapter->hw, TCTL, tctl);
 
        e1000_config_collision_dist(&adapter->hw);
 
-       /* Setup Transmit Descriptor Settings for this adapter */
-       adapter->txd_cmd = E1000_TXD_CMD_IFCS | E1000_TXD_CMD_IDE;
+       /* Setup Transmit Descriptor Settings for eop descriptor */
+       adapter->txd_cmd = E1000_TXD_CMD_IDE | E1000_TXD_CMD_EOP |
+               E1000_TXD_CMD_IFCS;
 
        if(adapter->hw.report_tx_early == 1)
                adapter->txd_cmd |= E1000_TXD_CMD_RS;
        else
                adapter->txd_cmd |= E1000_TXD_CMD_RPS;
+
+       /* Cache if we're 82544 running in PCI-X because we'll
+        * need this to apply a workaround later in the send path. */
+       if(adapter->hw.mac_type == e1000_82544 &&
+          adapter->hw.bus_type == e1000_bus_type_pcix)
+               adapter->pcix_82544 = 1;
 }
 
 /**
@@ -843,8 +915,8 @@ e1000_setup_rctl(struct e1000_adapter *adapter)
        rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
 
        rctl |= E1000_RCTL_EN | E1000_RCTL_BAM |
-               E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
-               (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT);
+               E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+               (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT);
 
        if(adapter->hw.tbi_compatibility_on == 1)
                rctl |= E1000_RCTL_SBP;
@@ -898,12 +970,9 @@ e1000_configure_rx(struct e1000_adapter *adapter)
 
        if(adapter->hw.mac_type >= e1000_82540) {
                E1000_WRITE_REG(&adapter->hw, RADV, adapter->rx_abs_int_delay);
-
-               /* Set the interrupt throttling rate.  Value is calculated
-                * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns) */
-#define MAX_INTS_PER_SEC        8000
-#define DEFAULT_ITR             1000000000/(MAX_INTS_PER_SEC * 256)
-               E1000_WRITE_REG(&adapter->hw, ITR, DEFAULT_ITR);
+               if(adapter->itr > 1)
+                       E1000_WRITE_REG(&adapter->hw, ITR,
+                               1000000000 / (adapter->itr * 256));
        }
 
        /* Setup the Base and Length of the Rx Descriptor Ring */
@@ -961,35 +1030,38 @@ e1000_free_tx_resources(struct e1000_adapter *adapter)
 static void
 e1000_clean_tx_ring(struct e1000_adapter *adapter)
 {
+       struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
+       struct e1000_buffer *buffer_info;
        struct pci_dev *pdev = adapter->pdev;
        unsigned long size;
-       int i;
+       unsigned int i;
 
        /* Free all the Tx ring sk_buffs */
 
-       for(i = 0; i < adapter->tx_ring.count; i++) {
-               if(adapter->tx_ring.buffer_info[i].skb) {
+       for(i = 0; i < tx_ring->count; i++) {
+               buffer_info = &tx_ring->buffer_info[i];
+               if(buffer_info->skb) {
 
                        pci_unmap_page(pdev,
-                                      adapter->tx_ring.buffer_info[i].dma,
-                                      adapter->tx_ring.buffer_info[i].length,
+                                      buffer_info->dma,
+                                      buffer_info->length,
                                       PCI_DMA_TODEVICE);
 
-                       dev_kfree_skb(adapter->tx_ring.buffer_info[i].skb);
+                       dev_kfree_skb(buffer_info->skb);
 
-                       adapter->tx_ring.buffer_info[i].skb = NULL;
+                       buffer_info->skb = NULL;
                }
        }
 
-       size = sizeof(struct e1000_buffer) * adapter->tx_ring.count;
-       memset(adapter->tx_ring.buffer_info, 0, size);
+       size = sizeof(struct e1000_buffer) * tx_ring->count;
+       memset(tx_ring->buffer_info, 0, size);
 
        /* Zero out the descriptor ring */
 
-       memset(adapter->tx_ring.desc, 0, adapter->tx_ring.size);
+       memset(tx_ring->desc, 0, tx_ring->size);
 
-       adapter->tx_ring.next_to_use = 0;
-       adapter->tx_ring.next_to_clean = 0;
+       tx_ring->next_to_use = 0;
+       tx_ring->next_to_clean = 0;
 
        E1000_WRITE_REG(&adapter->hw, TDH, 0);
        E1000_WRITE_REG(&adapter->hw, TDT, 0);
@@ -1005,17 +1077,17 @@ e1000_clean_tx_ring(struct e1000_adapter *adapter)
 static void
 e1000_free_rx_resources(struct e1000_adapter *adapter)
 {
+       struct e1000_desc_ring *rx_ring = &adapter->rx_ring;
        struct pci_dev *pdev = adapter->pdev;
 
        e1000_clean_rx_ring(adapter);
 
-       kfree(adapter->rx_ring.buffer_info);
-       adapter->rx_ring.buffer_info = NULL;
+       kfree(rx_ring->buffer_info);
+       rx_ring->buffer_info = NULL;
 
-       pci_free_consistent(pdev, adapter->rx_ring.size,
-                           adapter->rx_ring.desc, adapter->rx_ring.dma);
+       pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, rx_ring->dma);
 
-       adapter->rx_ring.desc = NULL;
+       rx_ring->desc = NULL;
 }
 
 /**
@@ -1026,35 +1098,38 @@ e1000_free_rx_resources(struct e1000_adapter *adapter)
 static void
 e1000_clean_rx_ring(struct e1000_adapter *adapter)
 {
+       struct e1000_desc_ring *rx_ring = &adapter->rx_ring;
+       struct e1000_buffer *buffer_info;
        struct pci_dev *pdev = adapter->pdev;
        unsigned long size;
-       int i;
+       unsigned int i;
 
        /* Free all the Rx ring sk_buffs */
 
-       for(i = 0; i < adapter->rx_ring.count; i++) {
-               if(adapter->rx_ring.buffer_info[i].skb) {
+       for(i = 0; i < rx_ring->count; i++) {
+               buffer_info = &rx_ring->buffer_info[i];
+               if(buffer_info->skb) {
 
                        pci_unmap_single(pdev,
-                                        adapter->rx_ring.buffer_info[i].dma,
-                                        adapter->rx_ring.buffer_info[i].length,
+                                        buffer_info->dma,
+                                        buffer_info->length,
                                         PCI_DMA_FROMDEVICE);
 
-                       dev_kfree_skb(adapter->rx_ring.buffer_info[i].skb);
+                       dev_kfree_skb(buffer_info->skb);
 
-                       adapter->rx_ring.buffer_info[i].skb = NULL;
+                       buffer_info->skb = NULL;
                }
        }
 
-       size = sizeof(struct e1000_buffer) * adapter->rx_ring.count;
-       memset(adapter->rx_ring.buffer_info, 0, size);
+       size = sizeof(struct e1000_buffer) * rx_ring->count;
+       memset(rx_ring->buffer_info, 0, size);
 
        /* Zero out the descriptor ring */
 
-       memset(adapter->rx_ring.desc, 0, adapter->rx_ring.size);
+       memset(rx_ring->desc, 0, rx_ring->size);
 
-       adapter->rx_ring.next_to_clean = 0;
-       adapter->rx_ring.next_to_use = 0;
+       rx_ring->next_to_clean = 0;
+       rx_ring->next_to_use = 0;
 
        E1000_WRITE_REG(&adapter->hw, RDH, 0);
        E1000_WRITE_REG(&adapter->hw, RDT, 0);
@@ -1141,7 +1216,7 @@ e1000_set_mac(struct net_device *netdev, void *p)
  *
  * The set_multi entry point is called whenever the multicast address
  * list or the network interface flags are updated.  This routine is
- * resposible for configuring the hardware for proper multicast,
+ * responsible for configuring the hardware for proper multicast,
  * promiscuous mode, and all-multi behavior.
  **/
 
@@ -1175,9 +1250,9 @@ e1000_set_multi(struct net_device *netdev)
        if(hw->mac_type == e1000_82542_rev2_0)
                e1000_enter_82542_rst(adapter);
 
-       /* load the first 15 multicast address into the exact filters 1-15
+       /* load the first 14 multicast address into the exact filters 1-14
         * RAR 0 is used for the station MAC adddress
-        * if there are not 15 addresses, go ahead and clear the filters
+        * if there are not 14 addresses, go ahead and clear the filters
         */
        mc_ptr = netdev->mc_list;
 
@@ -1207,6 +1282,40 @@ e1000_set_multi(struct net_device *netdev)
                e1000_leave_82542_rst(adapter);
 }
 
+static void
+e1000_tx_flush(struct e1000_adapter *adapter)
+{
+       uint32_t ctrl, tctl, txcw, icr;
+
+       e1000_irq_disable(adapter);
+
+       if(adapter->hw.mac_type < e1000_82543) {
+               /* Transmit Unit Reset */
+               tctl = E1000_READ_REG(&adapter->hw, TCTL);
+               E1000_WRITE_REG(&adapter->hw, TCTL, tctl | E1000_TCTL_RST);
+               E1000_WRITE_REG(&adapter->hw, TCTL, tctl);
+               e1000_clean_tx_ring(adapter);
+               e1000_configure_tx(adapter);
+       } else {
+               txcw = E1000_READ_REG(&adapter->hw, TXCW);
+               E1000_WRITE_REG(&adapter->hw, TXCW, txcw & ~E1000_TXCW_ANE);
+
+               ctrl = E1000_READ_REG(&adapter->hw, CTRL);
+               E1000_WRITE_REG(&adapter->hw, CTRL, ctrl | E1000_CTRL_SLU |
+                               E1000_CTRL_ILOS);
+
+               mdelay(10);
+
+               e1000_clean_tx_irq(adapter);
+               E1000_WRITE_REG(&adapter->hw, CTRL, ctrl);
+               E1000_WRITE_REG(&adapter->hw, TXCW, txcw);
+
+               /* clear the link status change interrupts this caused */
+               icr = E1000_READ_REG(&adapter->hw, ICR);
+       }
+
+       e1000_irq_enable(adapter);
+}
 
 /* need to wait a few seconds after link up to get diagnostic information from the phy */
 
@@ -1217,6 +1326,48 @@ e1000_update_phy_info(unsigned long data)
        e1000_phy_get_info(&adapter->hw, &adapter->phy_info);
 }
 
+/**
+ * e1000_82547_tx_fifo_stall - Timer Call-back
+ * @data: pointer to adapter cast into an unsigned long
+ **/
+
+static void
+e1000_82547_tx_fifo_stall(unsigned long data)
+{
+       struct e1000_adapter *adapter = (struct e1000_adapter *) data;
+       struct net_device *netdev = adapter->netdev;
+       uint32_t tctl;
+
+       if(atomic_read(&adapter->tx_fifo_stall)) {
+               if((E1000_READ_REG(&adapter->hw, TDT) ==
+                   E1000_READ_REG(&adapter->hw, TDH)) &&
+                  (E1000_READ_REG(&adapter->hw, TDFT) ==
+                   E1000_READ_REG(&adapter->hw, TDFH)) &&
+                  (E1000_READ_REG(&adapter->hw, TDFTS) ==
+                   E1000_READ_REG(&adapter->hw, TDFHS))) {
+                       tctl = E1000_READ_REG(&adapter->hw, TCTL);
+                       E1000_WRITE_REG(&adapter->hw, TCTL,
+                                       tctl & ~E1000_TCTL_EN);
+                       E1000_WRITE_REG(&adapter->hw, TDFT,
+                                       adapter->tx_head_addr);
+                       E1000_WRITE_REG(&adapter->hw, TDFH,
+                                       adapter->tx_head_addr);
+                       E1000_WRITE_REG(&adapter->hw, TDFTS,
+                                       adapter->tx_head_addr);
+                       E1000_WRITE_REG(&adapter->hw, TDFHS,
+                                       adapter->tx_head_addr);
+                       E1000_WRITE_REG(&adapter->hw, TCTL, tctl);
+                       E1000_WRITE_FLUSH(&adapter->hw);
+
+                       adapter->tx_fifo_head = 0;
+                       atomic_set(&adapter->tx_fifo_stall, 0);
+                       netif_wake_queue(netdev);
+               } else {
+                       mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1);
+               }
+       }
+}
+
 /**
  * e1000_watchdog - Timer Call-back
  * @data: pointer to netdev cast into an unsigned long
@@ -1228,7 +1379,7 @@ e1000_watchdog(unsigned long data)
        struct e1000_adapter *adapter = (struct e1000_adapter *) data;
        struct net_device *netdev = adapter->netdev;
        struct e1000_desc_ring *txdr = &adapter->tx_ring;
-       int i;
+       unsigned int i;
 
        e1000_check_for_link(&adapter->hw);
 
@@ -1247,6 +1398,7 @@ e1000_watchdog(unsigned long data)
                        netif_carrier_on(netdev);
                        netif_wake_queue(netdev);
                        mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ);
+                       adapter->smartspeed = 0;
                }
        } else {
                if(netif_carrier_ok(netdev)) {
@@ -1259,11 +1411,34 @@ e1000_watchdog(unsigned long data)
                        netif_stop_queue(netdev);
                        mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ);
                }
+
+               e1000_smartspeed(adapter);
        }
 
        e1000_update_stats(adapter);
        e1000_update_adaptive(&adapter->hw);
 
+       if(!netif_carrier_ok(netdev)) {
+               if(E1000_DESC_UNUSED(txdr) + 1 < txdr->count) {
+                       unsigned long flags;
+                       spin_lock_irqsave(&netdev->xmit_lock, flags);
+                       e1000_tx_flush(adapter);
+                       spin_unlock_irqrestore(&netdev->xmit_lock, flags);
+               }
+       }
+
+       /* Dynamic mode for Interrupt Throttle Rate (ITR) */
+       if(adapter->hw.mac_type >= e1000_82540 && adapter->itr == 1) {
+               /* Symmetric Tx/Rx gets a reduced ITR=2000; Total
+                * asymmetrical Tx or Rx gets ITR=8000; everyone
+                * else is between 2000-8000. */
+               uint32_t goc = (adapter->gotcl + adapter->gorcl) / 10000;
+               uint32_t dif = (adapter->gotcl > adapter->gorcl ? 
+                       adapter->gotcl - adapter->gorcl :
+                       adapter->gorcl - adapter->gotcl) / 10000;
+               uint32_t itr = goc > 0 ? (dif * 6000 / goc + 2000) : 8000;
+               E1000_WRITE_REG(&adapter->hw, ITR, 1000000000 / (itr * 256));
+       }
 
        /* Cause software interrupt to ensure rx ring is cleaned */
        E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_RXDMT0);
@@ -1281,14 +1456,67 @@ e1000_watchdog(unsigned long data)
 
 #define E1000_TX_FLAGS_CSUM            0x00000001
 #define E1000_TX_FLAGS_VLAN            0x00000002
+#define E1000_TX_FLAGS_TSO             0x00000004
 #define E1000_TX_FLAGS_VLAN_MASK       0xffff0000
 #define E1000_TX_FLAGS_VLAN_SHIFT      16
 
+static inline boolean_t
+e1000_tso(struct e1000_adapter *adapter, struct sk_buff *skb)
+{
+#ifdef NETIF_F_TSO
+       struct e1000_context_desc *context_desc;
+       unsigned int i;
+       uint8_t ipcss, ipcso, tucss, tucso, hdr_len;
+       uint16_t ipcse, tucse, mss;
+
+       if(skb_shinfo(skb)->tso_size) {
+               hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+               mss = skb_shinfo(skb)->tso_size;
+               skb->nh.iph->tot_len = 0;
+               skb->nh.iph->check = 0;
+               skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr,
+                                                     skb->nh.iph->daddr,
+                                                     0,
+                                                     IPPROTO_TCP,
+                                                     0);
+               ipcss = skb->nh.raw - skb->data;
+               ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data;
+               ipcse = skb->h.raw - skb->data - 1;
+               tucss = skb->h.raw - skb->data;
+               tucso = (void *)&(skb->h.th->check) - (void *)skb->data;
+               tucse = 0;
+
+               i = adapter->tx_ring.next_to_use;
+               context_desc = E1000_CONTEXT_DESC(adapter->tx_ring, i);
+
+               context_desc->lower_setup.ip_fields.ipcss  = ipcss;
+               context_desc->lower_setup.ip_fields.ipcso  = ipcso;
+               context_desc->lower_setup.ip_fields.ipcse  = cpu_to_le16(ipcse);
+               context_desc->upper_setup.tcp_fields.tucss = tucss;
+               context_desc->upper_setup.tcp_fields.tucso = tucso;
+               context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse);
+               context_desc->tcp_seg_setup.fields.mss     = cpu_to_le16(mss);
+               context_desc->tcp_seg_setup.fields.hdr_len = hdr_len;
+               context_desc->cmd_and_length = cpu_to_le32(
+                       E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
+                       E1000_TXD_CMD_IP | E1000_TXD_CMD_TCP |
+                       (skb->len - (hdr_len)));
+
+               if(++i == adapter->tx_ring.count) i = 0;
+               adapter->tx_ring.next_to_use = i;
+
+               return TRUE;
+       }
+#endif
+
+       return FALSE;
+}
+
 static inline boolean_t
 e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb)
 {
        struct e1000_context_desc *context_desc;
-       int i;
+       unsigned int i;
        uint8_t css, cso;
 
        if(skb->ip_summed == CHECKSUM_HW) {
@@ -1302,10 +1530,9 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb)
                context_desc->upper_setup.tcp_fields.tucso = cso;
                context_desc->upper_setup.tcp_fields.tucse = 0;
                context_desc->tcp_seg_setup.data = 0;
-               context_desc->cmd_and_length =
-                       cpu_to_le32(adapter->txd_cmd | E1000_TXD_CMD_DEXT);
+               context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT);
 
-               i = (i + 1) % adapter->tx_ring.count;
+               if(++i == adapter->tx_ring.count) i = 0;
                adapter->tx_ring.next_to_use = i;
 
                return TRUE;
@@ -1314,59 +1541,135 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb)
        return FALSE;
 }
 
+#define E1000_MAX_TXD_PWR      12
+#define E1000_MAX_DATA_PER_TXD (1<<E1000_MAX_TXD_PWR)
+
 static inline int
-e1000_tx_map(struct e1000_adapter *adapter, struct sk_buff *skb)
+e1000_tx_map(struct e1000_adapter *adapter, struct sk_buff *skb,
+       unsigned int first)
 {
        struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
-       int len, offset, size, count, i;
-
-       int f;
-       len = skb->len - skb->data_len;
-       i = (tx_ring->next_to_use + tx_ring->count - 1) % tx_ring->count;
-       count = 0;
+       struct e1000_buffer *buffer_info;
+       unsigned int len = skb->len, max_per_txd = E1000_MAX_DATA_PER_TXD;
+       unsigned int offset = 0, size, count = 0, i;
+
+#ifdef MAX_SKB_FRAGS
+#ifdef NETIF_F_TSO
+       unsigned int mss = skb_shinfo(skb)->tso_size;
+       /* The controller does a simple calculation to 
+        * make sure there is enough room in the FIFO before
+        * initiating the DMA for each buffer.  The calc is:
+        * 4 = ceil(buffer len/mss).  To make sure we don't
+        * overrun the FIFO, adjust the max buffer len if mss
+        * drops. */
+       if(mss) max_per_txd = min(mss << 2, max_per_txd);
+#endif
+       unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+       unsigned int f;
+       len -= skb->data_len;
+#endif
 
-       offset = 0;
+       i = tx_ring->next_to_use;
 
        while(len) {
-               i = (i + 1) % tx_ring->count;
-               size = min(len, adapter->max_data_per_txd);
-               tx_ring->buffer_info[i].length = size;
-               tx_ring->buffer_info[i].dma =
+               buffer_info = &tx_ring->buffer_info[i];
+               size = min(len, max_per_txd);
+#ifdef NETIF_F_TSO
+               /* Workaround for premature desc write-backs
+                * in TSO mode.  Append 4-byte sentinel desc */
+               if(mss && !nr_frags && size == len && size > 8)
+                       size -= 4;
+#endif
+               /* Workaround for potential 82544 hang in PCI-X.  Avoid
+                * terminating buffers within evenly-aligned dwords. */
+               if(adapter->pcix_82544 &&
+                  !((unsigned long)(skb->data + offset + size - 1) & 4) &&
+                  size > 4)
+                       size -= 4;
+
+               buffer_info->length = size;
+               buffer_info->dma =
                        pci_map_single(adapter->pdev,
                                skb->data + offset,
                                size,
                                PCI_DMA_TODEVICE);
-               tx_ring->buffer_info[i].time_stamp = jiffies;
+               buffer_info->time_stamp = jiffies;
 
                len -= size;
                offset += size;
                count++;
+               if(++i == tx_ring->count) i = 0;
        }
 
-       for(f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
+#ifdef MAX_SKB_FRAGS
+       for(f = 0; f < nr_frags; f++) {
                struct skb_frag_struct *frag;
 
                frag = &skb_shinfo(skb)->frags[f];
                len = frag->size;
-               offset = 0;
+               offset = frag->page_offset;
 
                while(len) {
-                       i = (i + 1) % tx_ring->count;
-                       size = min(len, adapter->max_data_per_txd);
-                       tx_ring->buffer_info[i].length = size;
-                       tx_ring->buffer_info[i].dma =
+                       buffer_info = &tx_ring->buffer_info[i];
+                       size = min(len, max_per_txd);
+#ifdef NETIF_F_TSO
+                       /* Workaround for premature desc write-backs
+                        * in TSO mode.  Append 4-byte sentinel desc */
+                       if(mss && f == (nr_frags-1) && size == len && size > 8)
+                               size -= 4;
+#endif
+                       /* Workaround for potential 82544 hang in PCI-X.
+                        * Avoid terminating buffers within evenly-aligned
+                        * dwords. */
+                       if(adapter->pcix_82544 &&
+                          !((unsigned long)(frag->page+offset+size-1) & 4) &&
+                          size > 4)
+                               size -= 4;
+
+                       buffer_info->length = size;
+                       buffer_info->dma =
                                pci_map_page(adapter->pdev,
                                        frag->page,
-                                       frag->page_offset + offset,
+                                       offset,
                                        size,
                                        PCI_DMA_TODEVICE);
+                       buffer_info->time_stamp = jiffies;
 
                        len -= size;
                        offset += size;
                        count++;
+                       if(++i == tx_ring->count) i = 0;
+               }
+       }
+#endif
+
+       if(E1000_DESC_UNUSED(&adapter->tx_ring) < count) {
+
+               /* There aren't enough descriptors available to queue up
+                * this send, so undo the mapping and abort the send. 
+                * We could have done the check before we mapped the skb,
+                * but because of all the workarounds (above), it's too
+                * difficult to predict how many we're going to need.*/
+               i = first;
+
+               while(count--) {
+                       buffer_info = &tx_ring->buffer_info[i];
+                       if(buffer_info->dma) {
+                               pci_unmap_page(adapter->pdev,
+                                              buffer_info->dma,
+                                              buffer_info->length,
+                                              PCI_DMA_TODEVICE);
+                               buffer_info->dma = 0;
+                       }
+                       if(++i == tx_ring->count) i = 0;
                }
+
+               return 0;
        }
+
+       i = (i == 0) ? tx_ring->count - 1 : i - 1;
        tx_ring->buffer_info[i].skb = skb;
+       tx_ring->buffer_info[first].next_to_watch = i;
 
        return count;
 }
@@ -1376,11 +1679,15 @@ e1000_tx_queue(struct e1000_adapter *adapter, int count, int tx_flags)
 {
        struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
        struct e1000_tx_desc *tx_desc = NULL;
-       uint32_t txd_upper, txd_lower;
-       int i;
-
-       txd_upper = 0;
-       txd_lower = adapter->txd_cmd;
+       struct e1000_buffer *buffer_info;
+       uint32_t txd_upper = 0, txd_lower = E1000_TXD_CMD_IFCS;
+       unsigned int i;
+
+       if(tx_flags & E1000_TX_FLAGS_TSO) {
+               txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D |
+                            E1000_TXD_CMD_TSE;
+               txd_upper |= (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8;
+       }
 
        if(tx_flags & E1000_TX_FLAGS_CSUM) {
                txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
@@ -1395,15 +1702,16 @@ e1000_tx_queue(struct e1000_adapter *adapter, int count, int tx_flags)
        i = tx_ring->next_to_use;
 
        while(count--) {
+               buffer_info = &tx_ring->buffer_info[i];
                tx_desc = E1000_TX_DESC(*tx_ring, i);
-               tx_desc->buffer_addr = cpu_to_le64(tx_ring->buffer_info[i].dma);
+               tx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
                tx_desc->lower.data =
-                       cpu_to_le32(txd_lower | tx_ring->buffer_info[i].length);
+                       cpu_to_le32(txd_lower | buffer_info->length);
                tx_desc->upper.data = cpu_to_le32(txd_upper);
-               i = (i + 1) % tx_ring->count;
+               if(++i == tx_ring->count) i = 0;
        }
 
-       tx_desc->lower.data |= cpu_to_le32(E1000_TXD_CMD_EOP);
+       tx_desc->lower.data |= cpu_to_le32(adapter->txd_cmd);
 
        /* Force memory writes to complete before letting h/w
         * know there are new descriptors to fetch.  (Only
@@ -1415,56 +1723,87 @@ e1000_tx_queue(struct e1000_adapter *adapter, int count, int tx_flags)
        E1000_WRITE_REG(&adapter->hw, TDT, i);
 }
 
-#define TXD_USE_COUNT(S, X) (((S) / (X)) + (((S) % (X)) ? 1 : 0))
+/**
+ * 82547 workaround to avoid controller hang in half-duplex environment.
+ * The workaround is to avoid queuing a large packet that would span
+ * the internal Tx FIFO ring boundary by notifying the stack to resend
+ * the packet at a later time.  This gives the Tx FIFO an opportunity to
+ * flush all packets.  When that occurs, we reset the Tx FIFO pointers
+ * to the beginning of the Tx FIFO.
+ **/
+
+#define E1000_FIFO_HDR                 0x10
+#define E1000_82547_PAD_LEN            0x3E0
+
+static inline int
+e1000_82547_fifo_workaround(struct e1000_adapter *adapter, struct sk_buff *skb)
+{
+       uint32_t fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head;
+       uint32_t skb_fifo_len = skb->len + E1000_FIFO_HDR;
+
+       E1000_ROUNDUP(skb_fifo_len, E1000_FIFO_HDR);
+
+       if(adapter->link_duplex != HALF_DUPLEX)
+               goto no_fifo_stall_required;
+
+       if(atomic_read(&adapter->tx_fifo_stall))
+               return 1;
+
+       if(skb_fifo_len >= (E1000_82547_PAD_LEN + fifo_space)) {
+               atomic_set(&adapter->tx_fifo_stall, 1);
+               return 1;
+       }
+
+no_fifo_stall_required:
+       adapter->tx_fifo_head += skb_fifo_len;
+       if(adapter->tx_fifo_head >= adapter->tx_fifo_size)
+               adapter->tx_fifo_head -= adapter->tx_fifo_size;
+       return 0;
+}
 
 static int
 e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 {
        struct e1000_adapter *adapter = netdev->priv;
-       int tx_flags = 0, count;
-       int f;
+       unsigned int first;
+       unsigned int tx_flags = 0;
+       int count;
 
-       count = TXD_USE_COUNT(skb->len - skb->data_len,
-                             adapter->max_data_per_txd);
-
-       if(count == 0) {
+       if(skb->len <= 0) {
                dev_kfree_skb_any(skb);
                return 0;
        }
 
-       for(f = 0; f < skb_shinfo(skb)->nr_frags; f++)
-               count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size,
-                                      adapter->max_data_per_txd);
-
-       if(skb->ip_summed == CHECKSUM_HW)
-               count++;
-
-       if(E1000_DESC_UNUSED(&adapter->tx_ring) < count) {
-               printk("%s: BUG! Ring full with queue awake!\n", netdev->name);
-               netif_stop_queue(netdev);
-               return 1;
+       if(adapter->hw.mac_type == e1000_82547) {
+               if(e1000_82547_fifo_workaround(adapter, skb)) {
+                       netif_stop_queue(netdev);
+                       mod_timer(&adapter->tx_fifo_stall_timer, jiffies);
+                       return 1;
+               }
        }
 
-       if(e1000_tx_csum(adapter, skb))
-               tx_flags |= E1000_TX_FLAGS_CSUM;
-
+#ifdef NETIF_F_HW_VLAN_TX
        if(adapter->vlgrp && vlan_tx_tag_present(skb)) {
                tx_flags |= E1000_TX_FLAGS_VLAN;
-               tx_flags |= (vlan_tx_tag_get(skb)<<E1000_TX_FLAGS_VLAN_SHIFT);
+               tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT);
        }
+#endif
 
-       spin_lock_irq(&adapter->tx_lock);
-
-       count = e1000_tx_map(adapter, skb);
-
-       e1000_tx_queue(adapter, count, tx_flags);
-
-       netdev->trans_start = jiffies;
+       first = adapter->tx_ring.next_to_use;
+       
+       if(e1000_tso(adapter, skb))
+               tx_flags |= E1000_TX_FLAGS_TSO;
+       else if(e1000_tx_csum(adapter, skb))
+               tx_flags |= E1000_TX_FLAGS_CSUM;
 
-       if(E1000_DESC_UNUSED(&adapter->tx_ring) < (MAX_SKB_FRAGS + 1))
+       if((count = e1000_tx_map(adapter, skb, first)))
+               e1000_tx_queue(adapter, count, tx_flags);
+       else {
                netif_stop_queue(netdev);
+               return 1;
+       }
 
-       spin_unlock_irq(&adapter->tx_lock);
+       netdev->trans_start = jiffies;
 
        return 0;
 }
@@ -1477,11 +1816,11 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 static void
 e1000_tx_timeout(struct net_device *netdev)
 {
-    //struct e1000_adapter *adapter = netdev->priv;
+       struct e1000_adapter *adapter = netdev->priv;
 
        /* Do the reset outside of interrupt context */
-       //schedule_task(&adapter->tx_timeout_task); XXXX Not in Xen!!!
-       e1000_tx_timeout_task(netdev);  // XXX HACK
+       //schedule_work(&adapter->tx_timeout_task);
+       e1000_tx_timeout_task(netdev); // XXXX HACK!!! XEN
 }
 
 static void
@@ -1584,7 +1923,8 @@ e1000_update_stats(struct e1000_adapter *adapter)
 
        adapter->stats.crcerrs += E1000_READ_REG(hw, CRCERRS);
        adapter->stats.gprc += E1000_READ_REG(hw, GPRC);
-       adapter->stats.gorcl += E1000_READ_REG(hw, GORCL);
+       adapter->gorcl = E1000_READ_REG(hw, GORCL);
+       adapter->stats.gorcl += adapter->gorcl;
        adapter->stats.gorch += E1000_READ_REG(hw, GORCH);
        adapter->stats.bprc += E1000_READ_REG(hw, BPRC);
        adapter->stats.mprc += E1000_READ_REG(hw, MPRC);
@@ -1615,7 +1955,8 @@ e1000_update_stats(struct e1000_adapter *adapter)
        adapter->stats.xofftxc += E1000_READ_REG(hw, XOFFTXC);
        adapter->stats.fcruc += E1000_READ_REG(hw, FCRUC);
        adapter->stats.gptc += E1000_READ_REG(hw, GPTC);
-       adapter->stats.gotcl += E1000_READ_REG(hw, GOTCL);
+       adapter->gotcl = E1000_READ_REG(hw, GOTCL);
+       adapter->stats.gotcl += adapter->gotcl;
        adapter->stats.gotch += E1000_READ_REG(hw, GOTCH);
        adapter->stats.rnbc += E1000_READ_REG(hw, RNBC);
        adapter->stats.ruc += E1000_READ_REG(hw, RUC);
@@ -1693,6 +2034,7 @@ e1000_update_stats(struct e1000_adapter *adapter)
                }
 
                if((hw->mac_type <= e1000_82546) &&
+                  (hw->phy_type == e1000_phy_m88) &&
                   !e1000_read_phy_reg(hw, M88E1000_RX_ERR_CNTR, &phy_tmp))
                        adapter->phy_stats.receive_errors += phy_tmp;
        }
@@ -1709,7 +2051,7 @@ e1000_irq_disable(struct e1000_adapter *adapter)
        atomic_inc(&adapter->irq_sem);
        E1000_WRITE_REG(&adapter->hw, IMC, ~0);
        E1000_WRITE_FLUSH(&adapter->hw);
-       synchronize_irq();
+       synchronize_irq(adapter->netdev->irq);
 }
 
 /**
@@ -1733,82 +2075,155 @@ e1000_irq_enable(struct e1000_adapter *adapter)
  * @pt_regs: CPU registers structure
  **/
 
-static void
+static irqreturn_t
 e1000_intr(int irq, void *data, struct pt_regs *regs)
 {
        struct net_device *netdev = data;
        struct e1000_adapter *adapter = netdev->priv;
-       uint32_t icr;
-       int i = E1000_MAX_INTR;
+       uint32_t icr = E1000_READ_REG(&adapter->hw, ICR);
+#ifndef CONFIG_E1000_NAPI
+       unsigned int i;
+#endif
 
-       while(i && (icr = E1000_READ_REG(&adapter->hw, ICR))) {
+       if(!icr)
+               return IRQ_NONE;  /* Not our interrupt */
 
-               if(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
-                       adapter->hw.get_link_status = 1;
-                       mod_timer(&adapter->watchdog_timer, jiffies);
-               }
+       if(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
+               adapter->hw.get_link_status = 1;
+               mod_timer(&adapter->watchdog_timer, jiffies);
+       }
 
-               e1000_clean_rx_irq(adapter);
-               e1000_clean_tx_irq(adapter);
-               i--;
+#ifdef CONFIG_E1000_NAPI
+       if(netif_rx_schedule_prep(netdev)) {
+
+               /* Disable interrupts and register for poll. The flush 
+                 of the posted write is intentionally left out.
+               */
 
+               atomic_inc(&adapter->irq_sem);
+               E1000_WRITE_REG(&adapter->hw, IMC, ~0);
+               __netif_rx_schedule(netdev);
        }
+#else
+       for(i = 0; i < E1000_MAX_INTR; i++)
+               if(!e1000_clean_rx_irq(adapter) &
+                  !e1000_clean_tx_irq(adapter))
+                       break;
+#endif
+#ifdef E1000_COUNT_ICR
+       adapter->icr_txdw += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_txqe += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_lsc += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_rxseq += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_rxdmt += icr & 0x01;
+       icr >>= 2;
+       adapter->icr_rxo += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_rxt += icr & 0x01;
+       icr >>= 2;
+       adapter->icr_mdac += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_rxcfg += icr & 0x01;
+       icr >>= 1;
+       adapter->icr_gpi += icr & 0x01;
+#endif
+
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_E1000_NAPI
+/**
+ * e1000_clean - NAPI Rx polling callback
+ * @adapter: board private structure
+ **/
+
+static int
+e1000_clean(struct net_device *netdev, int *budget)
+{
+       struct e1000_adapter *adapter = netdev->priv;
+       int work_to_do = min(*budget, netdev->quota);
+       int work_done = 0;
+       
+       e1000_clean_tx_irq(adapter);
+       e1000_clean_rx_irq(adapter, &work_done, work_to_do);
+
+       *budget -= work_done;
+       netdev->quota -= work_done;
+       
+       if(work_done < work_to_do) {
+               netif_rx_complete(netdev);
+               e1000_irq_enable(adapter);
+       }
+
+       return (work_done >= work_to_do);
 }
+#endif
 
 /**
  * e1000_clean_tx_irq - Reclaim resources after transmit completes
  * @adapter: board private structure
  **/
 
-static void
+static boolean_t
 e1000_clean_tx_irq(struct e1000_adapter *adapter)
 {
        struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
        struct net_device *netdev = adapter->netdev;
        struct pci_dev *pdev = adapter->pdev;
-       struct e1000_tx_desc *tx_desc;
-       int i;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adapter->tx_lock, flags);
+       struct e1000_tx_desc *tx_desc, *eop_desc;
+       struct e1000_buffer *buffer_info;
+       unsigned int i, eop;
+       boolean_t cleaned = FALSE;
 
        i = tx_ring->next_to_clean;
-       tx_desc = E1000_TX_DESC(*tx_ring, i);
+       eop = tx_ring->buffer_info[i].next_to_watch;
+       eop_desc = E1000_TX_DESC(*tx_ring, eop);
 
-       while(tx_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) {
+       while(eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) {
 
-               if(tx_ring->buffer_info[i].dma) {
+               for(cleaned = FALSE; !cleaned; ) {
+                       tx_desc = E1000_TX_DESC(*tx_ring, i);
+                       buffer_info = &tx_ring->buffer_info[i];
 
-                       pci_unmap_page(pdev,
-                                      tx_ring->buffer_info[i].dma,
-                                      tx_ring->buffer_info[i].length,
-                                      PCI_DMA_TODEVICE);
+                       if(buffer_info->dma) {
 
-                       tx_ring->buffer_info[i].dma = 0;
-               }
+                               pci_unmap_page(pdev,
+                                              buffer_info->dma,
+                                              buffer_info->length,
+                                              PCI_DMA_TODEVICE);
 
-               if(tx_ring->buffer_info[i].skb) {
+                               buffer_info->dma = 0;
+                       }
 
-                       dev_kfree_skb_any(tx_ring->buffer_info[i].skb);
+                       if(buffer_info->skb) {
 
-                       tx_ring->buffer_info[i].skb = NULL;
-               }
+                               dev_kfree_skb_any(buffer_info->skb);
 
-               tx_desc->upper.data = 0;
+                               buffer_info->skb = NULL;
+                       }
 
-               i = (i + 1) % tx_ring->count;
-               tx_desc = E1000_TX_DESC(*tx_ring, i);
+                       tx_desc->buffer_addr = 0;
+                       tx_desc->lower.data = 0;
+                       tx_desc->upper.data = 0;
+
+                       cleaned = (i == eop);
+                       if(++i == tx_ring->count) i = 0;
+               }
+               
+               eop = tx_ring->buffer_info[i].next_to_watch;
+               eop_desc = E1000_TX_DESC(*tx_ring, eop);
        }
 
        tx_ring->next_to_clean = i;
 
-       if(netif_queue_stopped(netdev) && netif_carrier_ok(netdev) &&
-          (E1000_DESC_UNUSED(tx_ring) > E1000_TX_QUEUE_WAKE)) {
-
+       if(cleaned && netif_queue_stopped(netdev) && netif_carrier_ok(netdev))
                netif_wake_queue(netdev);
-       }
 
-       spin_unlock_irqrestore(&adapter->tx_lock, flags);
+       return cleaned;
 }
 
 /**
@@ -1816,30 +2231,47 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter)
  * @adapter: board private structure
  **/
 
-static void
+static boolean_t
+#ifdef CONFIG_E1000_NAPI
+e1000_clean_rx_irq(struct e1000_adapter *adapter, int *work_done,
+                   int work_to_do)
+#else
 e1000_clean_rx_irq(struct e1000_adapter *adapter)
+#endif
 {
        struct e1000_desc_ring *rx_ring = &adapter->rx_ring;
        struct net_device *netdev = adapter->netdev;
        struct pci_dev *pdev = adapter->pdev;
        struct e1000_rx_desc *rx_desc;
+       struct e1000_buffer *buffer_info;
        struct sk_buff *skb;
        unsigned long flags;
        uint32_t length;
        uint8_t last_byte;
-       int i;
+       unsigned int i;
+       boolean_t cleaned = FALSE;
 
        i = rx_ring->next_to_clean;
        rx_desc = E1000_RX_DESC(*rx_ring, i);
 
        while(rx_desc->status & E1000_RXD_STAT_DD) {
+               buffer_info = &rx_ring->buffer_info[i];
+
+#ifdef CONFIG_E1000_NAPI
+               if(*work_done >= work_to_do)
+                       break;
+
+               (*work_done)++;
+#endif
+
+               cleaned = TRUE;
 
                pci_unmap_single(pdev,
-                                rx_ring->buffer_info[i].dma,
-                                rx_ring->buffer_info[i].length,
+                                buffer_info->dma,
+                                buffer_info->length,
                                 PCI_DMA_FROMDEVICE);
 
-               skb = rx_ring->buffer_info[i].skb;
+               skb = buffer_info->skb;
                length = le16_to_cpu(rx_desc->length);
 
                if(!(rx_desc->status & E1000_RXD_STAT_EOP)) {
@@ -1850,9 +2282,9 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter)
 
                        dev_kfree_skb_irq(skb);
                        rx_desc->status = 0;
-                       rx_ring->buffer_info[i].skb = NULL;
+                       buffer_info->skb = NULL;
 
-                       i = (i + 1) % rx_ring->count;
+                       if(++i == rx_ring->count) i = 0;
 
                        rx_desc = E1000_RX_DESC(*rx_ring, i);
                        continue;
@@ -1878,9 +2310,9 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter)
 
                                dev_kfree_skb_irq(skb);
                                rx_desc->status = 0;
-                               rx_ring->buffer_info[i].skb = NULL;
+                               buffer_info->skb = NULL;
 
-                               i = (i + 1) % rx_ring->count;
+                               if(++i == rx_ring->count) i = 0;
 
                                rx_desc = E1000_RX_DESC(*rx_ring, i);
                                continue;
@@ -1894,18 +2326,37 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter)
                e1000_rx_checksum(adapter, rx_desc, skb);
 
                skb->protocol = eth_type_trans(skb, netdev);
+#ifdef CONFIG_E1000_NAPI
+#ifdef NETIF_F_HW_VLAN_TX
+               if(adapter->vlgrp && (rx_desc->status & E1000_RXD_STAT_VP)) {
+                       vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
+                               le16_to_cpu(rx_desc->special &
+                                       E1000_RXD_SPC_VLAN_MASK));
+               } else {
+                       netif_receive_skb(skb);
+               }
+#else
+               netif_receive_skb(skb);
+#endif
+#else /* CONFIG_E1000_NAPI */
+#ifdef NETIF_F_HW_VLAN_TX
                if(adapter->vlgrp && (rx_desc->status & E1000_RXD_STAT_VP)) {
                        vlan_hwaccel_rx(skb, adapter->vlgrp,
-                               (rx_desc->special & E1000_RXD_SPC_VLAN_MASK));
+                               le16_to_cpu(rx_desc->special &
+                                       E1000_RXD_SPC_VLAN_MASK));
                } else {
                        netif_rx(skb);
                }
+#else
+               netif_rx(skb);
+#endif
+#endif /* CONFIG_E1000_NAPI */
                netdev->last_rx = jiffies;
 
                rx_desc->status = 0;
-               rx_ring->buffer_info[i].skb = NULL;
+               buffer_info->skb = NULL;
 
-               i = (i + 1) % rx_ring->count;
+               if(++i == rx_ring->count) i = 0;
 
                rx_desc = E1000_RX_DESC(*rx_ring, i);
        }
@@ -1913,6 +2364,8 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter)
        rx_ring->next_to_clean = i;
 
        e1000_alloc_rx_buffers(adapter);
+
+       return cleaned;
 }
 
 /**
@@ -1927,15 +2380,15 @@ e1000_alloc_rx_buffers(struct e1000_adapter *adapter)
        struct net_device *netdev = adapter->netdev;
        struct pci_dev *pdev = adapter->pdev;
        struct e1000_rx_desc *rx_desc;
+       struct e1000_buffer *buffer_info;
        struct sk_buff *skb;
-       int reserve_len;
-       int i;
-
-       reserve_len = 2;
+       int reserve_len = 2;
+       unsigned int i;
 
        i = rx_ring->next_to_use;
+       buffer_info = &rx_ring->buffer_info[i];
 
-       while(!rx_ring->buffer_info[i].skb) {
+       while(!buffer_info->skb) {
                rx_desc = E1000_RX_DESC(*rx_ring, i);
 
                skb = dev_alloc_skb(adapter->rx_buffer_len + reserve_len);
@@ -1953,17 +2406,17 @@ e1000_alloc_rx_buffers(struct e1000_adapter *adapter)
 
                skb->dev = netdev;
 
-               rx_ring->buffer_info[i].skb = skb;
-               rx_ring->buffer_info[i].length = adapter->rx_buffer_len;
-               rx_ring->buffer_info[i].dma =
+               buffer_info->skb = skb;
+               buffer_info->length = adapter->rx_buffer_len;
+               buffer_info->dma =
                        pci_map_single(pdev,
                                       skb->data,
                                       adapter->rx_buffer_len,
                                       PCI_DMA_FROMDEVICE);
 
-               rx_desc->buffer_addr = cpu_to_le64(rx_ring->buffer_info[i].dma);
+               rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
 
-               if(!(i % E1000_RX_BUFFER_WRITE)) {
+               if((i & ~(E1000_RX_BUFFER_WRITE - 1)) == i) {
                        /* Force memory writes to complete before letting h/w
                         * know there are new descriptors to fetch.  (Only
                         * applicable for weak-ordered memory model archs,
@@ -1973,12 +2426,68 @@ e1000_alloc_rx_buffers(struct e1000_adapter *adapter)
                        E1000_WRITE_REG(&adapter->hw, RDT, i);
                }
 
-               i = (i + 1) % rx_ring->count;
+               if(++i == rx_ring->count) i = 0;
+               buffer_info = &rx_ring->buffer_info[i];
        }
 
        rx_ring->next_to_use = i;
 }
 
+/**
+ * e1000_smartspeed - Workaround for SmartSpeed on 82541 and 82547 controllers.
+ * @adapter:
+ **/
+
+static void
+e1000_smartspeed(struct e1000_adapter *adapter)
+{
+       uint16_t phy_status;
+       uint16_t phy_ctrl;
+
+       if((adapter->hw.phy_type != e1000_phy_igp) || !adapter->hw.autoneg ||
+          !(adapter->hw.autoneg_advertised & ADVERTISE_1000_FULL))
+               return;
+
+       if(adapter->smartspeed == 0) {
+               /* If Master/Slave config fault is asserted twice,
+                * we assume back-to-back */
+               e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_status);
+               if(!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+               e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_status);
+               if(!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+               e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_ctrl);
+               if(phy_ctrl & CR_1000T_MS_ENABLE) {
+                       phy_ctrl &= ~CR_1000T_MS_ENABLE;
+                       e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL,
+                                           phy_ctrl);
+                       adapter->smartspeed++;
+                       if(!e1000_phy_setup_autoneg(&adapter->hw) &&
+                          !e1000_read_phy_reg(&adapter->hw, PHY_CTRL,
+                                              &phy_ctrl)) {
+                               phy_ctrl |= (MII_CR_AUTO_NEG_EN |
+                                            MII_CR_RESTART_AUTO_NEG);
+                               e1000_write_phy_reg(&adapter->hw, PHY_CTRL,
+                                                   phy_ctrl);
+                       }
+               }
+               return;
+       } else if(adapter->smartspeed == E1000_SMARTSPEED_DOWNSHIFT) {
+               /* If still no link, perhaps using 2/3 pair cable */
+               e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_ctrl);
+               phy_ctrl |= CR_1000T_MS_ENABLE;
+               e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_ctrl);
+               if(!e1000_phy_setup_autoneg(&adapter->hw) &&
+                  !e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_ctrl)) {
+                       phy_ctrl |= (MII_CR_AUTO_NEG_EN |
+                                    MII_CR_RESTART_AUTO_NEG);
+                       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_ctrl);
+               }
+       }
+       /* Restart process after E1000_SMARTSPEED_MAX iterations */
+       if(adapter->smartspeed++ == E1000_SMARTSPEED_MAX)
+               adapter->smartspeed = 0;
+}
+
 /**
  * e1000_ioctl -
  * @netdev:
@@ -1990,13 +2499,103 @@ static int
 e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 {
        switch (cmd) {
+#ifdef SIOCGMIIPHY
+       case SIOCGMIIPHY:
+       case SIOCGMIIREG:
+       case SIOCSMIIREG:
+               return e1000_mii_ioctl(netdev, ifr, cmd);
+#endif
+#ifdef SIOCETHTOOL
        case SIOCETHTOOL:
                return e1000_ethtool_ioctl(netdev, ifr);
+#endif
        default:
                return -EOPNOTSUPP;
        }
 }
 
+#ifdef SIOCGMIIPHY
+/**
+ * e1000_mii_ioctl -
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ **/
+
+static int
+e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+       struct e1000_adapter *adapter = netdev->priv;
+       struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
+       int retval;
+       uint16_t mii_reg;
+       uint16_t spddplx;
+
+       if(adapter->hw.media_type != e1000_media_type_copper)
+               return -EOPNOTSUPP;
+
+       switch (cmd) {
+       case SIOCGMIIPHY:
+               data->phy_id = adapter->hw.phy_addr;
+               break;
+       case SIOCGMIIREG:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (e1000_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
+                                  &data->val_out))
+                       return -EIO;
+               break;
+       case SIOCSMIIREG:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (data->reg_num & ~(0x1F))
+                       return -EFAULT;
+               mii_reg = data->val_in;
+               if (e1000_write_phy_reg(&adapter->hw, data->reg_num,
+                                       data->val_in))
+                       return -EIO;
+               if (adapter->hw.phy_type == e1000_phy_m88) {
+                       switch (data->reg_num) {
+                       case PHY_CTRL:
+                               if(data->val_in & MII_CR_AUTO_NEG_EN) {
+                                       adapter->hw.autoneg = 1;
+                                       adapter->hw.autoneg_advertised = 0x2F;
+                               } else {
+                                       if (data->val_in & 0x40)
+                                               spddplx = SPEED_1000;
+                                       else if (data->val_in & 0x2000)
+                                               spddplx = SPEED_100;
+                                       else
+                                               spddplx = SPEED_10;
+                                       spddplx += (data->val_in & 0x100)
+                                                  ? FULL_DUPLEX :
+                                                  HALF_DUPLEX;
+                                       retval = e1000_set_spd_dplx(adapter,
+                                                                   spddplx);
+                                       if(retval)
+                                               return retval;
+                               }
+                               if(netif_running(adapter->netdev)) {
+                                       e1000_down(adapter);
+                                       e1000_up(adapter);
+                               } else
+                                       e1000_reset(adapter);
+                               break;
+                       case M88E1000_PHY_SPEC_CTRL:
+                       case M88E1000_EXT_PHY_SPEC_CTRL:
+                               if (e1000_phy_reset(&adapter->hw))
+                                       return -EIO;
+                               break;
+                       }
+               }
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return E1000_SUCCESS;
+}
+#endif
+
 /**
  * e1000_rx_checksum - Receive Checksum Offload for 82543
  * @adapter: board private structure
@@ -2037,7 +2636,13 @@ e1000_pci_set_mwi(struct e1000_hw *hw)
 {
        struct e1000_adapter *adapter = hw->back;
 
+#ifdef HAVE_PCI_SET_MWI
        pci_set_mwi(adapter->pdev);
+#else
+       pci_write_config_word(adapter->pdev, PCI_COMMAND,
+                             adapter->hw.pci_cmd_word |
+                             PCI_COMMAND_INVALIDATE);
+#endif
 }
 
 void
@@ -2045,7 +2650,13 @@ e1000_pci_clear_mwi(struct e1000_hw *hw)
 {
        struct e1000_adapter *adapter = hw->back;
 
+#ifdef HAVE_PCI_SET_MWI
        pci_clear_mwi(adapter->pdev);
+#else
+       pci_write_config_word(adapter->pdev, PCI_COMMAND,
+                             adapter->hw.pci_cmd_word &
+                             ~PCI_COMMAND_INVALIDATE);
+#endif
 }
 
 void
@@ -2076,6 +2687,7 @@ e1000_io_write(struct e1000_hw *hw, uint32_t port, uint32_t value)
        outl(value, port);
 }
 
+#ifdef NETIF_F_HW_VLAN_TX
 static void
 e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
 {
@@ -2166,6 +2778,36 @@ e1000_restore_vlan(struct e1000_adapter *adapter)
                }
        }
 }
+#endif
+
+int
+e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx)
+{
+       adapter->hw.autoneg = 0;
+
+       switch(spddplx) {
+       case SPEED_10 + DUPLEX_HALF:
+               adapter->hw.forced_speed_duplex = e1000_10_half;
+               break;
+       case SPEED_10 + DUPLEX_FULL:
+               adapter->hw.forced_speed_duplex = e1000_10_full;
+               break;
+       case SPEED_100 + DUPLEX_HALF:
+               adapter->hw.forced_speed_duplex = e1000_100_half;
+               break;
+       case SPEED_100 + DUPLEX_FULL:
+               adapter->hw.forced_speed_duplex = e1000_100_full;
+               break;
+       case SPEED_1000 + DUPLEX_FULL:
+               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
+               break;
+       case SPEED_1000 + DUPLEX_HALF: /* not supported */
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
 
 static int
 e1000_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
@@ -2176,7 +2818,7 @@ e1000_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
        case SYS_DOWN:
        case SYS_HALT:
        case SYS_POWER_OFF:
-               pci_for_each_dev(pdev) {
+               while((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
                        if(pci_dev_driver(pdev) == &e1000_driver)
                                e1000_suspend(pdev, 3);
                }
@@ -2223,7 +2865,8 @@ e1000_suspend(struct pci_dev *pdev, uint32_t state)
                        E1000_WRITE_REG(&adapter->hw, CTRL, ctrl);
                }
 
-               if(adapter->hw.media_type == e1000_media_type_fiber) {
+               if(adapter->hw.media_type == e1000_media_type_fiber ||
+                  adapter->hw.media_type == e1000_media_type_internal_serdes) {
                        /* keep the laser running in D3 */
                        ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
                        ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA;
@@ -2243,12 +2886,14 @@ e1000_suspend(struct pci_dev *pdev, uint32_t state)
 
        pci_save_state(pdev, adapter->pci_state);
 
-       if(adapter->hw.mac_type >= e1000_82540) {
+       if(adapter->hw.mac_type >= e1000_82540 &&
+          adapter->hw.media_type == e1000_media_type_copper) {
                manc = E1000_READ_REG(&adapter->hw, MANC);
                if(manc & E1000_MANC_SMBUS_EN) {
                        manc |= E1000_MANC_ARP_EN;
                        E1000_WRITE_REG(&adapter->hw, MANC, manc);
-                       state = 0;
+                       pci_enable_wake(pdev, 3, 1);
+                       pci_enable_wake(pdev, 4, 1); /* 4 == D3 cold */
                }
        }
 
@@ -2280,7 +2925,8 @@ e1000_resume(struct pci_dev *pdev)
 
        netif_device_attach(netdev);
 
-       if(adapter->hw.mac_type >= e1000_82540) {
+       if(adapter->hw.mac_type >= e1000_82540 &&
+          adapter->hw.media_type == e1000_media_type_copper) {
                manc = E1000_READ_REG(&adapter->hw, MANC);
                manc &= ~(E1000_MANC_ARP_EN);
                E1000_WRITE_REG(&adapter->hw, MANC, manc);
index 40b62bfecde373efad3ac68c09ebb2d82bf52462..649a01041206e357ffb6da590296c7d610b4bbb4 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -27,7 +27,7 @@
 *******************************************************************************/
 
 
-/* glue for the OS independant part of e1000
+/* glue for the OS independent part of e1000
  * includes register access macros
  */
 
 #include <asm/io.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
+#include "kcompat.h"
 
+#define usec_delay(x) udelay(x)
 #ifndef msec_delay
-#define msec_delay(x) {\
-       int s=jiffies+1+((x*HZ)/1000); \
-       while(jiffies<s); }
-
-#if 0
-/********************  NOT in XEN ! *******/
-#define XXXXmsec_delay(x)      do { if(in_interrupt()) { \
+#define msec_delay(x)  do { if(in_interrupt()) { \
                                /* Don't mdelay in interrupt context! */ \
                                BUG(); \
                        } else { \
                        } } while(0)
 #endif
 
-#else
-#error "msec already defined!"
-#endif
-
 #define PCI_COMMAND_REGISTER   PCI_COMMAND
 #define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE
 
 typedef enum {
+#undef FALSE
     FALSE = 0,
+#undef TRUE
     TRUE = 1
 } boolean_t;
 
+#undef ASSERT
+#define ASSERT(x)      if(!(x)) BUG()
 #define MSGOUT(S, A, B)        printk(KERN_DEBUG S "\n", A, B)
 
-//#define DBG 1
-
 #if DBG
 #define DEBUGOUT(S)            printk(KERN_DEBUG S "\n")
 #define DEBUGOUT1(S, A...)     printk(KERN_DEBUG S "\n", A)
@@ -88,24 +82,22 @@ typedef enum {
 
 
 #define E1000_WRITE_REG(a, reg, value) ( \
-    ((a)->mac_type >= e1000_82543) ? \
-        (writel((value), ((a)->hw_addr + E1000_##reg))) : \
-        (writel((value), ((a)->hw_addr + E1000_82542_##reg))))
+    writel((value), ((a)->hw_addr + \
+        (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg))))
 
 #define E1000_READ_REG(a, reg) ( \
-    ((a)->mac_type >= e1000_82543) ? \
-        readl((a)->hw_addr + E1000_##reg) : \
-        readl((a)->hw_addr + E1000_82542_##reg))
+    readl((a)->hw_addr + \
+        (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg)))
 
 #define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \
-    ((a)->mac_type >= e1000_82543) ? \
-        writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) : \
-        writel((value), ((a)->hw_addr + E1000_82542_##reg + ((offset) << 2))))
+    writel((value), ((a)->hw_addr + \
+        (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+        ((offset) << 2))))
 
 #define E1000_READ_REG_ARRAY(a, reg, offset) ( \
-    ((a)->mac_type >= e1000_82543) ? \
-        readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) : \
-        readl((a)->hw_addr + E1000_82542_##reg + ((offset) << 2)))
+    readl((a)->hw_addr + \
+        (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+        ((offset) << 2)))
 
 #define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, STATUS)
 
index a11941f3f2139120e2eb5c71aa2390a0d8a83586..d57f9ff558bd8ac66ee13faaf353861ca9f85d06 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -63,9 +63,10 @@ MODULE_PARM_DESC(X, S);
 /* Transmit Descriptor Count
  *
  * Valid Range: 80-256 for 82542 and 82543 gigabit ethernet controllers
- * Valid Range: 80-4096 for 82544
+ * Valid Range: 80-4096 for 82544 and newer
  *
- * Default Value: 256
+ * Default Value: 256 for 82542 and 82543 gigabit ethernet controllers
+ * Default Value: 1024 for 82544 and newer
  */
 
 E1000_PARAM(TxDescriptors, "Number of transmit descriptors");
@@ -73,9 +74,9 @@ E1000_PARAM(TxDescriptors, "Number of transmit descriptors");
 /* Receive Descriptor Count
  *
  * Valid Range: 80-256 for 82542 and 82543 gigabit ethernet controllers
- * Valid Range: 80-4096 for 82544
+ * Valid Range: 80-4096 for 82544 and newer
  *
- * Default Value: 80
+ * Default Value: 256
  */
 
 E1000_PARAM(RxDescriptors, "Number of receive descriptors");
@@ -140,7 +141,7 @@ E1000_PARAM(FlowControl, "Flow Control setting");
  * Valid Range: 0, 1
  *  - 0 - disables all checksum offload
  *  - 1 - enables receive IP/TCP/UDP checksum offload
- *        on 82543 based NICs
+ *        on 82543 and newer -based NICs
  *
  * Default Value: 1
  */
@@ -169,7 +170,7 @@ E1000_PARAM(TxAbsIntDelay, "Transmit Absolute Interrupt Delay");
  *
  * Valid Range: 0-65535
  *
- * Default Value: 0/128
+ * Default Value: 0
  */
 
 E1000_PARAM(RxIntDelay, "Receive Interrupt Delay");
@@ -183,6 +184,15 @@ E1000_PARAM(RxIntDelay, "Receive Interrupt Delay");
 
 E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay");
 
+/* Interrupt Throttle Rate (interrupts/sec)
+ *
+ * Valid Range: 100-100000 (0=off, 1=dynamic)
+ *
+ * Default Value: 1
+ */
+
+E1000_PARAM(InterruptThrottleRate, "Interrupt Throttling Rate");
+
 #define AUTONEG_ADV_DEFAULT  0x2F
 #define AUTONEG_ADV_MASK     0x2F
 #define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL
@@ -191,8 +201,9 @@ E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay");
 #define MAX_TXD                      256
 #define MIN_TXD                       80
 #define MAX_82544_TXD               4096
+#define DEFAULT_82544_TXD           1024
 
-#define DEFAULT_RXD                   80
+#define DEFAULT_RXD                  256
 #define MAX_RXD                      256
 #define MIN_RXD                       80
 #define MAX_82544_RXD               4096
@@ -213,6 +224,10 @@ E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay");
 #define MAX_TXABSDELAY            0xFFFF
 #define MIN_TXABSDELAY                 0
 
+#define DEFAULT_ITR                    1
+#define MAX_ITR                   100000
+#define MIN_ITR                      100
+
 struct e1000_option {
        enum { enable_option, range_option, list_option } type;
        char *name;
@@ -307,12 +322,15 @@ e1000_check_options(struct e1000_adapter *adapter)
                struct e1000_option opt = {
                        .type = range_option,
                        .name = "Transmit Descriptors",
-                       .err  = "using default of " __MODULE_STRING(DEFAULT_TXD),
-                       .def  = DEFAULT_TXD,
                        .arg  = { .r = { .min = MIN_TXD }}
                };
                struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
                e1000_mac_type mac_type = adapter->hw.mac_type;
+               opt.err = mac_type < e1000_82544 ?
+                       "using default of " __MODULE_STRING(DEFAULT_TXD) :
+                       "using default of " __MODULE_STRING(DEFAULT_82544_TXD);
+               opt.def = mac_type < e1000_82544 ?
+                       DEFAULT_TXD : DEFAULT_82544_TXD;
                opt.arg.r.max = mac_type < e1000_82544 ?
                        MAX_TXD : MAX_82544_TXD;
 
@@ -362,7 +380,8 @@ e1000_check_options(struct e1000_adapter *adapter)
                        .name = "Flow Control",
                        .err  = "reading default settings from EEPROM",
                        .def  = e1000_fc_default,
-                       .arg  = { .l = { .nr = ARRAY_SIZE(fc_list), .p = fc_list }}
+                       .arg  = { .l = { .nr = ARRAY_SIZE(fc_list),
+                                        .p = fc_list }}
                };
 
                int fc = FlowControl[bd];
@@ -370,60 +389,81 @@ e1000_check_options(struct e1000_adapter *adapter)
                adapter->hw.fc = adapter->hw.original_fc = fc;
        }
        { /* Transmit Interrupt Delay */
-               char *tidv = "using default of " __MODULE_STRING(DEFAULT_TIDV);
                struct e1000_option opt = {
                        .type = range_option,
                        .name = "Transmit Interrupt Delay",
-                       .arg  = { .r = { .min = MIN_TXDELAY, .max = MAX_TXDELAY }}
+                       .err  = "using default of " __MODULE_STRING(DEFAULT_TIDV),
+                       .def  = DEFAULT_TIDV,
+                       .arg  = { .r = { .min = MIN_TXDELAY,
+                                        .max = MAX_TXDELAY }}
                };
-               opt.def = DEFAULT_TIDV;
-               opt.err = tidv;
 
                adapter->tx_int_delay = TxIntDelay[bd];
                e1000_validate_option(&adapter->tx_int_delay, &opt);
        }
        { /* Transmit Absolute Interrupt Delay */
-               char *tadv = "using default of " __MODULE_STRING(DEFAULT_TADV);
                struct e1000_option opt = {
                        .type = range_option,
                        .name = "Transmit Absolute Interrupt Delay",
-                       .arg  = { .r = { .min = MIN_TXABSDELAY, .max = MAX_TXABSDELAY }}
+                       .err  = "using default of " __MODULE_STRING(DEFAULT_TADV),
+                       .def  = DEFAULT_TADV,
+                       .arg  = { .r = { .min = MIN_TXABSDELAY,
+                                        .max = MAX_TXABSDELAY }}
                };
-               opt.def = DEFAULT_TADV;
-               opt.err = tadv;
 
                adapter->tx_abs_int_delay = TxAbsIntDelay[bd];
                e1000_validate_option(&adapter->tx_abs_int_delay, &opt);
        }
        { /* Receive Interrupt Delay */
-               char *rdtr = "using default of " __MODULE_STRING(DEFAULT_RDTR);
                struct e1000_option opt = {
                        .type = range_option,
                        .name = "Receive Interrupt Delay",
-                       .arg  = { .r = { .min = MIN_RXDELAY, .max = MAX_RXDELAY }}
+                       .err  = "using default of " __MODULE_STRING(DEFAULT_RDTR),
+                       .def  = DEFAULT_RDTR,
+                       .arg  = { .r = { .min = MIN_RXDELAY,
+                                        .max = MAX_RXDELAY }}
                };
-               opt.def = DEFAULT_RDTR;
-               opt.err = rdtr;
 
                adapter->rx_int_delay = RxIntDelay[bd];
                e1000_validate_option(&adapter->rx_int_delay, &opt);
        }
        { /* Receive Absolute Interrupt Delay */
-               char *radv = "using default of " __MODULE_STRING(DEFAULT_RADV);
                struct e1000_option opt = {
                        .type = range_option,
                        .name = "Receive Absolute Interrupt Delay",
-                       .arg  = { .r = { .min = MIN_RXABSDELAY, .max = MAX_RXABSDELAY }}
+                       .err  = "using default of " __MODULE_STRING(DEFAULT_RADV),
+                       .def  = DEFAULT_RADV,
+                       .arg  = { .r = { .min = MIN_RXABSDELAY,
+                                        .max = MAX_RXABSDELAY }}
                };
-               opt.def = DEFAULT_RADV;
-               opt.err = radv;
 
                adapter->rx_abs_int_delay = RxAbsIntDelay[bd];
                e1000_validate_option(&adapter->rx_abs_int_delay, &opt);
        }
-       
+       { /* Interrupt Throttling Rate */
+               struct e1000_option opt = {
+                       .type = range_option,
+                       .name = "Interrupt Throttling Rate (ints/sec)",
+                       .err  = "using default of " __MODULE_STRING(DEFAULT_ITR),
+                       .def  = DEFAULT_ITR,
+                       .arg  = { .r = { .min = MIN_ITR,
+                                        .max = MAX_ITR }}
+               };
+
+               adapter->itr = InterruptThrottleRate[bd];
+               if(adapter->itr == 0) {
+                       printk(KERN_INFO "%s turned off\n", opt.name);
+               } else if(adapter->itr == 1 || adapter->itr == -1) {
+                       /* Dynamic mode */
+                       adapter->itr = 1;
+               } else {
+                       e1000_validate_option(&adapter->itr, &opt);
+               }
+       }
+
        switch(adapter->hw.media_type) {
        case e1000_media_type_fiber:
+       case e1000_media_type_internal_serdes:
                e1000_check_fiber_options(adapter);
                break;
        case e1000_media_type_copper:
@@ -486,7 +526,8 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
                        .name = "Speed",
                        .err  = "parameter ignored",
                        .def  = 0,
-                       .arg  = { .l = { .nr = ARRAY_SIZE(speed_list), .p = speed_list }}
+                       .arg  = { .l = { .nr = ARRAY_SIZE(speed_list),
+                                        .p = speed_list }}
                };
 
                speed = Speed[bd];
@@ -502,7 +543,8 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
                        .name = "Duplex",
                        .err  = "parameter ignored",
                        .def  = 0,
-                       .arg  = { .l = { .nr = ARRAY_SIZE(dplx_list), .p = dplx_list }}
+                       .arg  = { .l = { .nr = ARRAY_SIZE(dplx_list),
+                                        .p = dplx_list }}
                };
 
                dplx = Duplex[bd];
@@ -554,7 +596,8 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
                        .name = "AutoNeg",
                        .err  = "parameter ignored",
                        .def  = AUTONEG_ADV_DEFAULT,
-                       .arg  = { .l = { .nr = ARRAY_SIZE(an_list), .p = an_list }}
+                       .arg  = { .l = { .nr = ARRAY_SIZE(an_list),
+                                        .p = an_list }}
                };
 
                int an = AutoNeg[bd];
@@ -564,7 +607,7 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
 
        switch (speed + dplx) {
        case 0:
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET)
                        printk(KERN_INFO
                               "Speed and duplex autonegotiation enabled\n");
@@ -572,14 +615,14 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
        case HALF_DUPLEX:
                printk(KERN_INFO "Half Duplex specified without Speed\n");
                printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
                                                 ADVERTISE_100_HALF;
                break;
        case FULL_DUPLEX:
                printk(KERN_INFO "Full Duplex specified without Speed\n");
                printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_10_FULL |
                                                 ADVERTISE_100_FULL |
                                                 ADVERTISE_1000_FULL;
@@ -587,38 +630,38 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
        case SPEED_10:
                printk(KERN_INFO "10 Mbps Speed specified without Duplex\n");
                printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
                                                 ADVERTISE_10_FULL;
                break;
        case SPEED_10 + HALF_DUPLEX:
                printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n");
-               adapter->hw.autoneg = 0;
+               adapter->hw.autoneg = adapter->fc_autoneg = 0;
                adapter->hw.forced_speed_duplex = e1000_10_half;
                adapter->hw.autoneg_advertised = 0;
                break;
        case SPEED_10 + FULL_DUPLEX:
                printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n");
-               adapter->hw.autoneg = 0;
+               adapter->hw.autoneg = adapter->fc_autoneg = 0;
                adapter->hw.forced_speed_duplex = e1000_10_full;
                adapter->hw.autoneg_advertised = 0;
                break;
        case SPEED_100:
                printk(KERN_INFO "100 Mbps Speed specified without Duplex\n");
                printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_100_HALF |
                                                 ADVERTISE_100_FULL;
                break;
        case SPEED_100 + HALF_DUPLEX:
                printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n");
-               adapter->hw.autoneg = 0;
+               adapter->hw.autoneg = adapter->fc_autoneg = 0;
                adapter->hw.forced_speed_duplex = e1000_100_half;
                adapter->hw.autoneg_advertised = 0;
                break;
        case SPEED_100 + FULL_DUPLEX:
                printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n");
-               adapter->hw.autoneg = 0;
+               adapter->hw.autoneg = adapter->fc_autoneg = 0;
                adapter->hw.forced_speed_duplex = e1000_100_full;
                adapter->hw.autoneg_advertised = 0;
                break;
@@ -626,20 +669,20 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
                printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n");
                printk(KERN_INFO
                       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
                break;
        case SPEED_1000 + HALF_DUPLEX:
                printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n");
                printk(KERN_INFO
                       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
                break;
        case SPEED_1000 + FULL_DUPLEX:
                printk(KERN_INFO
                       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-               adapter->hw.autoneg = 1;
+               adapter->hw.autoneg = adapter->fc_autoneg = 1;
                adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
                break;
        default:
diff --git a/xen/drivers/net/e1000/kcompat.c b/xen/drivers/net/e1000/kcompat.c
new file mode 100644 (file)
index 0000000..384b1f7
--- /dev/null
@@ -0,0 +1,170 @@
+/*******************************************************************************
+
+  
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
+  
+  This program is free software; you can redistribute it and/or modify it 
+  under the terms of the GNU General Public License as published by the Free 
+  Software Foundation; either version 2 of the License, or (at your option) 
+  any later version.
+  
+  This program is distributed in the hope that it will be useful, but WITHOUT 
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+  more details.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "kcompat.h"
+
+/*****************************************************************************/
+/* 2.4.13 => 2.4.3 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) )
+
+/**************************************/
+/* PCI DMA MAPPING */
+
+#if defined(CONFIG_HIGHMEM)
+
+#ifndef PCI_DRAM_OFFSET
+#define PCI_DRAM_OFFSET 0
+#endif
+
+u64 _kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, size_t size, int direction)
+{
+       return (((u64)(page - mem_map) << PAGE_SHIFT) + offset + PCI_DRAM_OFFSET);
+}
+
+#else /* CONFIG_HIGHMEM */
+
+u64 _kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, size_t size, int direction)
+{
+       return pci_map_single(dev, (void *)page_address(page) + offset, size, direction);
+}
+
+#endif /* CONFIG_HIGHMEM */
+
+void _kc_pci_unmap_page(struct pci_dev *dev, u64 dma_addr, size_t size, int direction)
+{
+       return pci_unmap_single(dev, dma_addr, size, direction);
+}
+
+#endif /* 2.4.13 => 2.4.3 */
+
+
+/*****************************************************************************/
+/* 2.4.3 => 2.4.0 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) )
+
+/**************************************/
+/* PCI DRIVER API */
+
+int _kc_pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask)
+{
+       if(!pci_dma_supported(dev, mask))
+               return -EIO;
+       dev->dma_mask = mask;
+       return 0;
+}
+
+int _kc_pci_request_regions(struct pci_dev *dev, char *res_name)
+{
+       int i;
+
+       for (i = 0; i < 6; i++) {
+               if (pci_resource_len(dev, i) == 0)
+                       continue;
+
+               if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
+                       if (!request_region(pci_resource_start(dev, i), pci_resource_len(dev, i), res_name)) {
+                               pci_release_regions(dev);
+                               return -EBUSY;
+                       }
+               } else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) {
+                       if (!request_mem_region(pci_resource_start(dev, i), pci_resource_len(dev, i), res_name)) {
+                               pci_release_regions(dev);
+                               return -EBUSY;
+                       }
+               }
+       }
+       return 0;
+}
+
+void _kc_pci_release_regions(struct pci_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < 6; i++) {
+               if (pci_resource_len(dev, i) == 0)
+                       continue;
+
+               if (pci_resource_flags(dev, i) & IORESOURCE_IO)
+                       release_region(pci_resource_start(dev, i), pci_resource_len(dev, i));
+
+               else if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
+                       release_mem_region(pci_resource_start(dev, i), pci_resource_len(dev, i));
+       }
+}
+
+/**************************************/
+/* NETWORK DRIVER API */
+
+struct net_device * _kc_alloc_etherdev(int sizeof_priv)
+{
+       struct net_device *dev;
+       int alloc_size;
+
+       alloc_size = sizeof (*dev) + sizeof_priv + IFNAMSIZ + 31;
+
+       dev = kmalloc(alloc_size, GFP_KERNEL);
+
+       if (!dev) return NULL;
+
+       memset(dev, 0, alloc_size);
+
+       if (sizeof_priv)
+               dev->priv = (void *) (((unsigned long)(dev + 1) + 31) & ~31);
+
+       dev->name[0] = '\0';
+
+       ether_setup(dev);
+
+       return dev;
+}
+
+int _kc_is_valid_ether_addr(u8 *addr)
+{
+       const char zaddr[6] = {0,};
+
+       return !(addr[0]&1) && memcmp( addr, zaddr, 6);
+}
+
+#endif /* 2.4.3 => 2.4.0 */
+
+
+/*****************************************************************************/
+/* 2.4.6 => 2.4.3 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,6) )
+
+int _kc_pci_set_power_state(struct pci_dev *dev, int state)
+{ return 0; }
+int _kc_pci_save_state(struct pci_dev *dev, u32 *buffer)
+{ return 0; }
+int _kc_pci_restore_state(struct pci_dev *pdev, u32 *buffer)
+{ return 0; }
+int _kc_pci_enable_wake(struct pci_dev *pdev, u32 state, int enable)
+{ return 0; }
+
+#endif /* 2.4.6 => 2.4.3 */
+
diff --git a/xen/drivers/net/e1000/kcompat.h b/xen/drivers/net/e1000/kcompat.h
new file mode 100644 (file)
index 0000000..602b840
--- /dev/null
@@ -0,0 +1,307 @@
+/*******************************************************************************
+
+  
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
+  
+  This program is free software; you can redistribute it and/or modify it 
+  under the terms of the GNU General Public License as published by the Free 
+  Software Foundation; either version 2 of the License, or (at your option) 
+  any later version.
+  
+  This program is distributed in the hope that it will be useful, but WITHOUT 
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+  more details.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _KCOMPAT_H_
+#define _KCOMPAT_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+//#include <linux/pagemap.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+
+#ifndef IRQ_HANDLED
+#define irqreturn_t void
+#define IRQ_HANDLED
+#define IRQ_NONE
+#endif
+
+#ifndef SET_NETDEV_DEV
+#define SET_NETDEV_DEV(net, pdev)
+#endif
+
+/*****************************************************************************/
+#ifndef unlikely
+#define unlikely(_x) _x
+#define likely(_x) _x
+#endif
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* Installations with ethtool version without eeprom, adapter id, or statistics
+ * support */
+#ifndef ETHTOOL_GSTATS
+#define ETHTOOL_GSTATS 0x1d
+#undef ethtool_drvinfo
+#define ethtool_drvinfo k_ethtool_drvinfo
+struct k_ethtool_drvinfo {
+       uint32_t cmd;
+       char     driver[32];
+       char     version[32];
+       char     fw_version[32];
+       char     bus_info[32];
+       char     reserved1[32];
+       char     reserved2[16];
+       uint32_t n_stats;
+       uint32_t testinfo_len;
+       uint32_t eedump_len;
+       uint32_t regdump_len;
+};
+
+struct ethtool_stats {
+       uint32_t cmd;
+       uint32_t n_stats;
+       uint64_t data[0];
+};
+
+#ifndef ETHTOOL_PHYS_ID
+#define ETHTOOL_PHYS_ID 0x1c
+#ifndef ETHTOOL_GSTRINGS
+#define ETHTOOL_GSTRINGS 0x1b
+enum ethtool_stringset {
+       ETH_SS_TEST             = 0,
+       ETH_SS_STATS,
+};
+struct ethtool_gstrings {
+       u32     cmd;            /* ETHTOOL_GSTRINGS */
+       u32     string_set;     /* string set id e.c. ETH_SS_TEST, etc*/
+       u32     len;            /* number of strings in the string set */
+       u8      data[0];
+};
+#ifndef ETHTOOL_TEST
+#define ETHTOOL_TEST 0x1a
+enum ethtool_test_flags {
+       ETH_TEST_FL_OFFLINE     = (1 << 0),
+       ETH_TEST_FL_FAILED      = (1 << 1),
+};
+struct ethtool_test {
+       uint32_t cmd;
+       uint32_t flags;
+       uint32_t reserved;
+       uint32_t len;
+       uint64_t data[0];
+};
+#ifndef ETHTOOL_GEEPROM
+#define ETHTOOL_GEEPROM 0xb
+#undef ETHTOOL_GREGS
+struct ethtool_eeprom {
+       uint32_t cmd;
+       uint32_t magic;
+       uint32_t offset;
+       uint32_t len;
+       uint8_t  data[0];
+};
+
+struct ethtool_value {
+       uint32_t cmd;
+       uint32_t data;
+};
+
+#ifndef ETHTOOL_GLINK
+#define ETHTOOL_GLINK 0xa
+#endif /* Ethtool version without link support */
+#endif /* Ethtool version without eeprom support */
+#endif /* Ethtool version without test support */
+#endif /* Ethtool version without strings support */
+#endif /* Ethtool version wihtout adapter id support */
+#endif /* Ethtool version without statistics support */
+
+/*****************************************************************************/
+/* 2.4.3 => 2.4.0 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) )
+
+/**************************************/
+/* PCI DRIVER API */
+
+#ifndef pci_set_dma_mask
+#define pci_set_dma_mask _kc_pci_set_dma_mask
+extern int _kc_pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask);
+#endif
+
+#ifndef pci_request_regions
+#define pci_request_regions _kc_pci_request_regions
+extern int _kc_pci_request_regions(struct pci_dev *pdev, char *res_name);
+#endif
+
+#ifndef pci_release_regions
+#define pci_release_regions _kc_pci_release_regions
+extern void _kc_pci_release_regions(struct pci_dev *pdev);
+#endif
+
+/**************************************/
+/* NETWORK DRIVER API */
+
+#ifndef alloc_etherdev
+#define alloc_etherdev _kc_alloc_etherdev
+extern struct net_device * _kc_alloc_etherdev(int sizeof_priv);
+#endif
+
+#ifndef is_valid_ether_addr
+#define is_valid_ether_addr _kc_is_valid_ether_addr
+extern int _kc_is_valid_ether_addr(u8 *addr);
+#endif
+
+/**************************************/
+/* MISCELLANEOUS */
+
+#ifndef INIT_TQUEUE
+#define INIT_TQUEUE(_tq, _routine, _data)              \
+       do {                                            \
+               INIT_LIST_HEAD(&(_tq)->list);           \
+               (_tq)->sync = 0;                        \
+               (_tq)->routine = _routine;              \
+               (_tq)->data = _data;                    \
+       } while(0)
+#endif
+
+#endif /* 2.4.3 => 2.4.0 */
+
+/*****************************************************************************/
+/* 2.4.6 => 2.4.3 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,6) )
+
+#ifndef pci_set_power_state
+#define pci_set_power_state _kc_pci_set_power_state
+extern int _kc_pci_set_power_state(struct pci_dev *dev, int state);
+#endif
+
+#ifndef pci_save_state
+#define pci_save_state _kc_pci_save_state
+extern int _kc_pci_save_state(struct pci_dev *dev, u32 *buffer);
+#endif
+
+#ifndef pci_restore_state
+#define pci_restore_state _kc_pci_restore_state
+extern int _kc_pci_restore_state(struct pci_dev *pdev, u32 *buffer);
+#endif
+
+#ifndef pci_enable_wake
+#define pci_enable_wake _kc_pci_enable_wake
+extern int _kc_pci_enable_wake(struct pci_dev *pdev, u32 state, int enable);
+#endif
+
+/* PCI PM entry point syntax changed, so don't support suspend/resume */
+#undef CONFIG_PM
+
+#endif /* 2.4.6 => 2.4.3 */
+
+/*****************************************************************************/
+/* 2.4.10 => 2.4.6 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) )
+
+/**************************************/
+/* MODULE API */
+
+#ifndef MODULE_LICENSE
+       #define MODULE_LICENSE(X)
+#endif
+
+/**************************************/
+/* OTHER */
+
+#undef min
+#define min(x,y) ({ \
+       const typeof(x) _x = (x);       \
+       const typeof(y) _y = (y);       \
+       (void) (&_x == &_y);            \
+       _x < _y ? _x : _y; })
+
+#undef max
+#define max(x,y) ({ \
+       const typeof(x) _x = (x);       \
+       const typeof(y) _y = (y);       \
+       (void) (&_x == &_y);            \
+       _x > _y ? _x : _y; })
+
+#endif /* 2.4.10 -> 2.4.6 */
+
+
+/*****************************************************************************/
+/* 2.4.13 => 2.4.10 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) )
+
+/**************************************/
+/* PCI DMA MAPPING */
+
+#ifndef virt_to_page
+       #define virt_to_page(v) (mem_map + (virt_to_phys(v) >> PAGE_SHIFT))
+#endif
+
+#ifndef pci_map_page
+#define pci_map_page _kc_pci_map_page
+extern u64 _kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, size_t size, int direction);
+#endif
+
+#ifndef pci_unmap_page
+#define pci_unmap_page _kc_pci_unmap_page
+extern void _kc_pci_unmap_page(struct pci_dev *dev, u64 dma_addr, size_t size, int direction);
+#endif
+
+/* pci_set_dma_mask takes dma_addr_t, which is only 32-bits prior to 2.4.13 */
+
+#undef PCI_DMA_32BIT
+#define PCI_DMA_32BIT  0xffffffff
+#undef PCI_DMA_64BIT
+#define PCI_DMA_64BIT  0xffffffff
+
+#endif /* 2.4.13 => 2.4.10 */
+
+/*****************************************************************************/
+/* 2.4.17 => 2.4.12 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,17) )
+
+#ifndef __devexit_p
+       #define __devexit_p(x) &(x)
+#endif
+
+#endif /* 2.4.17 => 2.4.13 */
+
+/*****************************************************************************/
+/* 2.5.28 => 2.4.17 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,28) )
+
+static inline void _kc_synchronize_irq() { synchronize_irq(); }
+#undef synchronize_irq
+#define synchronize_irq(X) _kc_synchronize_irq()
+
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define INIT_WORK INIT_TQUEUE
+#define schedule_work schedule_task
+
+#endif /* 2.5.28 => 2.4.17 */
+
+#endif /* _KCOMPAT_H_ */
+